0 说在之前
jt 学长说的没错,SA 果然是写一次忘一次……
于是这次重新学了一次,发现之前的 Blog 问题比较多,于是重写一次算了
实际上这些东西就是变相重写 Oi Wiki 后缀数组那一页,不过是写给自己的罢了
「Jump up HIGH!!」
jt 学长说的没错,SA 果然是写一次忘一次……
于是这次重新学了一次,发现之前的 Blog 问题比较多,于是重写一次算了
实际上这些东西就是变相重写 Oi Wiki 后缀数组那一页,不过是写给自己的罢了
给定一个整数 $x(2\leq x \leq 10^9)$
求 $a,b$ 满足 $\gcd(a,b) + \operatorname{lcm}(a,b) = x$
多组解输出任意一个
这有啥说的,随便找个质因数让后输出 $p, x$ 就可以
// Woshiluo Luo<[email protected]>
// 2020/03/14 22:36:48
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
int t, x;
int main() {
scanf( "%d", &t );
while( t -- ) {
scanf( "%d", &x );
int sq = std::sqrt(x) + 1;
for( int i = 1; i <= sq; i ++ ) {
if( x % i == 0 ) {
int tmp = ( x / i ) - 1;
if( tmp == 0 )
continue;
printf( "%d %d\n", i, tmp * i );
break;
}
}
}
}
给你一个长度为 $n$ 的数列 $a$, 现在你可以复制无数个 $a$ 接到原来的数列后面
问这样接完后,最长严格上升子序列的长度
因为可以接无限次,所以直接输出数字个数就可以了
// Woshiluo Luo<[email protected]>
// 2020/03/14 22:42:47
#include <cstdio>
#include <cstring>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
const int N = 1e5 + 1e4;
int t;
int n;
int a[N];
int main() {
scanf( "%d", &t );
while( t -- ) {
scanf( "%d", &n );
for( int i = 1; i <= n; i ++ ) {
scanf( "%d", &a[i] );
}
std::sort( a + 1, a + n + 1 );
int cnt = 0, la = 0;
for( int i = 1; i <= n; i ++ ) {
if( i == 1 || a[i] != la ) {
la = a[i];
cnt ++;
}
}
printf( "%d\n", cnt );
}
}
给一棵节点数为 $n$ 的树
现在你要给每条边一个边权,要求:
现在定义 $MEX(u,v)$ 为 $u,v$ 之间的最短简单路径上没有出现的边权中的最小值
请给出一种边权方案,最小化 $\max{MEX(u,v)}$
考虑每条边会被算进去多少次,记为每条边的贡献
贡献越大给越大的边就可以了
实际上可以更加简化,因为无论如何一定有一个 $MEX \geq 2$
所以我们尽可能避免 $0,1,2$ 出现在一条边上即可
随便找一个度数 $\geq 2$ 的点,把这三个数字放上去即可
如果没有这种点,那么怎么摆都会有一条 $MEX=n-1$ 的路径
// Woshiluo Luo<[email protected]>
// 2020/03/14 22:52:18
#include <cstdio>
#include <cstring>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
const int N = 1e5 + 1e4;
// Edge Start
struct edge {
int to, next, val;
} e[ N << 1 ];
int ehead[N], ecnt;
inline void add_edge( int now, int to, int val ) {
ecnt ++;
e[ecnt].to = to;
e[ecnt].next = ehead[now];
e[ecnt].val = val;
ehead[now] = ecnt;
}
// Edge End
struct node { long long val; int id; } max[N];
bool cmp( node _a, node _b ) { return _a.val < _b.val; }
int n, x, y;
int son[N], val[N];
void dfs( int now, int la ) {
son[now] = 1;
for( int i = ehead[now]; i; i = e[i].next ) {
if( e[i].to == la )
continue;
dfs( e[i].to, now );
son[now] += son[ e[i].to ];
max[ e[i].val ].val = ( 1LL * son[ e[i].to ] * ( n - son[ e[i].to ] ) );
}
}
int main() {
#ifdef woshiluo
freopen( "c.in", "r", stdin );
freopen( "c.out", "w", stdout );
#endif
scanf( "%d", &n );
for( int i = 1, u, v; i < n; i ++ ) {
max[i].id = i;
scanf( "%d%d", &u, &v );
add_edge( u, v, i );
add_edge( v, u, i );
}
dfs( 1, 0 );
std::sort( max + 1, max + n, cmp );
for( int i = 1; i < n; i ++ ) {
val[ max[i].id ] = i - 1;
}
for( int i = 1; i < n; i ++ ) {
printf( "%d\n", val[i] );
}
}
给定两个整数 $u,v$ 求最短的数列 $a$ 满足
多解输出任意一个
无解输出 $-1$
这题感觉的 C 简单啊
满足 $xor$ 容易,满足 $v$ 的话需要难度
考虑最后 $xor$ 的结果受每个二进制位的和的奇偶性影响
因此可以确定每一位的奇偶性
然后随便搞一下满足 $v$ 就可以了
// Woshiluo Luo<[email protected]>
// 2020/03/14 23:39:20
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
unsigned long long u, v;
int bit_u[110], bit_less[110], bit_cnt[110];
std::vector<unsigned long long> ans;
inline void noans() {
printf( "-1\n" );
exit(0);
}
void to_bit( unsigned long long a, int bit[] ) {
int cnt = 0;
while( a ) {
bit[ ++ cnt ] = a & 1;
a >>= 1;
}
}
int main() {
scanf( "%llu%llu", &u, &v );
if( v < u )
noans();
to_bit( v - u, bit_less );
to_bit( u, bit_u );
for( int i = 1; i <= 100; i ++ ) {
bit_cnt[i] = bit_u[i];
}
int p = 64;
int cnt = 0;
while( p ) {
bit_cnt[p] += ( cnt - ( cnt & 1 ) );
cnt = cnt & 1;
if( bit_less[p] )
cnt ++;
p --; cnt <<= 1;
}
if( cnt != 0 )
noans();
bool flag = true;
while( flag ) {
flag = false;
unsigned long long out = 0;
unsigned long long p = 1;
for( int i = 1; i <= 64; i ++, p <<= 1 ) {
if( bit_cnt[i] ) {
out = out | p;
bit_cnt[i] --;
flag = true;
}
}
if( flag )
ans.push_back(out);
}
cnt = ans.size();
printf( "%d\n", cnt );
for( int i = 0; i < cnt; i ++ ) {
printf( "%llu ", ans[i] );
}
}
给你一个长度为 $n$ 的数列,保证数列中每个数字有不超过 $7$ 个因数
请求出最短的子序列,使子序列成绩为完全平方数
这题比 F 题难吧…
首先由 保证数列中每个数字有不超过 $7$ 个因数
得每个数不会有超过两个 $1$ 和其本身以外的质因数
然后我们可以先把每个数里的完全平方数因子先除掉,因为这些不会对答案造成影响
这样下来每个数的因子只有 1 和其本身以及两个质因数(前提是这个数不是质数)
然后建图,如果这个数是质数,将其和 $1$ 连接,如过不是将其的两个质因数连接
剩下就是求最小环
// Woshiluo Luo<[email protected]>
// 2020/03/15 22:08:53
#include <cmath>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
const int N = 1e6 + 1e4;
const int M = 1100;
const int INF = 0x3f3f3f3f;
int n, ans = INF;
int dis[N], fa[N];
bool marked[N], pool[N];
// Edge Start
struct edge {
int cur, to, next;
} e[ N << 1 ];
int ehead[N], ecnt = 1;
inline void add_edge( int now, int to ) {
ecnt ++;
e[ecnt].cur = now;
e[ecnt].to = to;
e[ecnt].next = ehead[now];
ehead[now] = ecnt;
}
// Edge End
int bfs( int st ) {
memset( dis, INF, sizeof(dis) );
memset( fa, 0, sizeof(fa) );
std::queue<int> q;
q.push(st); dis[st] = 0;
while( !q.empty() ) {
int cur = q.front(); q.pop();
for( int i = ehead[cur]; i; i = e[i].next ) {
if( e[i].to == fa[cur] )
continue;
if( dis[ e[i].to ] >= INF ) {
fa[ e[i].to ] = cur;
dis[ e[i].to ] = dis[cur] + 1;
q.push( e[i].to );
}
else {
return dis[cur] + dis[ e[i].to ] + 1;
}
}
}
return INF;
}
int main() {
scanf( "%d", &n );
for( int i = 1, x; i <= n; i ++ ) {
scanf( "%d", &x );
std::vector<int> a;
int cnt = std::sqrt(x) + 1;
while(cnt > 1) {
int cnt_pow = cnt * cnt;
while( x % cnt_pow == 0 )
x /= cnt_pow;
cnt --;
}
if( x == 1 )
chk_Min( ans, 1 );
if( pool[x] )
chk_Min( ans, 2 );
pool[x] = true;
int tmp = std::sqrt(x);
for( int j = 2; j <= tmp; j ++ ) {
if( x % j == 0 ) {
a.push_back(j);
a.push_back( x / j );
break;
}
}
if( a.size() == 0 ) {
add_edge( 1, x );
add_edge( x, 1 );
}
else if( a.size() != 0 ) {
add_edge( a[0], a[1] );
add_edge( a[1], a[0] );
}
}
if( ans != INF ) {
printf( "%d\n", ans );
return 0;
}
for( int i = 1; i <= 1000; i ++ ) {
if( ehead[i] )
chk_Min( ans, bfs(i) );
}
if( ans >= INF )
ans = -1;
printf( "%d\n", ans );
}
给定一个 $n$ 个节点的图
令 $glo=\lceil\sqrt{n}\rceil$
你要么找到一个长度和 $glo$ 相等的环
要么找到一个比 $glo$ 小的独立集
先试图找环,找不到肯定有独立集
// Woshiluo Luo<[email protected]>
// 2020/03/15 15:49:32
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
const int N = 1e5 + 1e4;
const int M = 2e5 + 1e4;
int n, m, need;
int dep[N];
bool vis[N];
std::stack<int> st;
// Edge Start
struct edge { int to, next; } e[ M << 1 ];
int ehead[N], ecnt;
inline void add_edge( int now, int to ) {
ecnt ++;
e[ecnt].to = to;
e[ecnt].next = ehead[now];
ehead[now] = ecnt;
}
// Edge End
void dfs( int now ) {
st.push(now);
dep[now] = st.size();
for( int i = ehead[now]; i; i = e[i].next ) {
if( dep[ e[i].to ] == 0 )
dfs( e[i].to );
else if( dep[now] - dep[ e[i].to ] + 1 >= need ) {
int size = dep[now] - dep[ e[i].to ] + 1;
printf( "%d\n%d\n", 2, size );
for( int i = 1; i <= size; i ++ ) {
printf( "%d ", st.top() );
st.pop();
}
exit(0);
}
}
if( !vis[now] ) {
for( int i = ehead[now]; i; i = e[i].next ) {
vis[ e[i].to ] = true;
}
}
st.pop();
}
int main() {
#ifdef woshiluo
freopen( "f.in", "r", stdin );
freopen( "f.out", "w", stdout );
#endif
scanf( "%d%d", &n, &m );
while( need * need < n )
need ++;
while( m -- ) {
int u, v;
scanf( "%d%d", &u, &v );
add_edge( u, v ); add_edge( v, u );
}
dfs(1);
printf( "%d\n", 1 );
int cnt = 0;
for( int i = 1; i <= n; i ++ ) {
if( vis[i] == false ) {
printf( "%d ", i );
cnt ++;
}
if( cnt == need )
break;
}
}
给你两个正多边形,一个 $n$ 条边 $A$ ,一个 $m$ 条边 $B$
问 $B$ 有没有可能被 $A$ 包含且所有顶点都与 $A$ 的某一个节点重合
有输出 YES
,否则输出 NO
判 $n \bmod m = 0$ 就可以了
#include <cstdio>
int T;
int n, m;
int main() {
scanf( "%d", &T );
while( T -- ) {
scanf( "%d%d", &n, &m );
printf( "%s\n", ( n % m == 0 )? "YES": "NO" );
}
}
给你一个长度为 $n$ 的序列 $a$, 你可以将 $a$ 重新排序,使其满足 $j – a_j \neq i – a_i$
输出任何一个即可
保证有解
$n \leq 100$
$$
\begin{aligned}
j – a_j & \neq i – a_i \\
j – i & \neq a_j – a_i
\end{aligned}
$$
直接从大到小输出
// Woshiluo Luo<[email protected]>
// 2020/03/09 22:40:32
#include <cstdio>
#include <cstring>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
const int N = 110;
int T;
int n;
int a[N];
int main() {
scanf( "%d", &T );
while( T -- ) {
scanf( "%d", &n );
for( int i = 1; i <= n; i ++ ) {
scanf( "%d", &a[i] );
}
std::sort( a + 1, a + n + 1 );
for( int i = n; i >= 1; i -- ) {
printf( "%d ", a[i] );
}
printf( "\n" );
}
}
给你两个长度为 $n$ 的数列 $a,v$
其中 $a$ 通过输入提供,$v$ 初始所有值为 $0$
接下来,对于第 $i$ 次操作(从 $0$ 计数),你可以
问你是否通过多次操作后,使 $v$ 变成 $a$
能输出 YES
,否则输出 NO
首先对于每个数字 $a$, 考虑其是否可以变成多个 $k^i$ 的和(不能有重复的 $i$)
能的话算出来,看看有没有和之前重复的,有就不可能
不能的话直接没有可能
// Woshiluo Luo<[email protected]>
// 2020/03/09 22:58:45
#include <cstdio>
#include <cstring>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
const int N = 110;
int T;
int n;
long long k;
bool vis[N];
long long a[N];
inline void wrong() {
printf( "NO\n" );
}
inline void right() {
printf( "YES\n" );
}
void calc() {
for( int i = 1; i <= n; i ++ ) {
if( a[i] == 0 ) {
continue;
}
long long cur = a[i];
int cnt = 1;
while( cur ) {
int tmp = cur % k;
if( tmp > 1 ) {
wrong();
return ;
}
if( tmp == 1 ) {
if( vis[cnt] == false )
vis[cnt] = true;
else {
wrong();
return ;
}
}
cur /= k;
cnt ++;
}
}
right();
}
int main() {
scanf( "%d", &T );
while( T -- ) {
memset( vis, 0, sizeof(vis) );
scanf( "%d%lld", &n, &k );
for( int i = 1; i <= n; i ++ ) {
scanf( "%lld", &a[i] );
}
calc();
}
}
你需要寻找这样的数列个数
个数对 $998244353$ 取模后输出
$2 \leq n \leq m \leq 2 \times 10^5$
式子题,看 Code 去
// Woshiluo Luo<[email protected]>
// 2020/03/09 23:21:18
#include <cstdio>
#include <cstring>
#include <algorithm>
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
const int N = 2e5 + 1e4;
const int mod = 998244353;
inline int add( int a, int b ) { return ( a + b ) % mod; }
inline int mul( int a, int b ) { return ( 1LL * a * b ) % mod; }
inline void add_eq( int &a, int b ) { a = ( a + b ) % mod; }
inline void mul_eq( int &a, int b ) { a = ( 1LL * a * b ) % mod; }
int ksm( int a, int p ) {
int res = 1;
while(p) {
if( p & 1 )
res = mul( res, a );
a = mul( a, a );
p >>= 1;
}
return res;
}
inline int get_inv( int a ) { return ksm( a, mod - 2 ); }
int n, m, sum, ans;
int fact[N], inv[N];
void init() {
fact[1] = 1;
for( int i = 2; i <= m; i ++ ) {
fact[i] = mul( fact[ i - 1 ], i );
}
inv[m] = get_inv( fact[m] );
for( int i = m - 1; i >= 1; i -- ) {
inv[i] = mul( inv[ i + 1 ], i + 1 );
}
inv[0] = 1;
fact[0] = 1;
}
// Get C^a_b
inline int C( int a, int b ) {
if( a <= 0 )
return 1;
if( b <= 0 )
return 1;
return mul( mul( fact[b], inv[ b - a ] ), inv[a] );
}
int main() {
#ifdef woshiluo
freopen( "d.in", "r", stdin );
freopen( "d.out", "w", stdout );
#endif
scanf( "%d%d", &n, &m );
init();
for( int i = n - 1; i <= m; i ++ ) {
add_eq( sum, mul( C( n - 3, i - 2 ), mul( m - i + 1, n - 2 ) ) );
}
for( int i = 2; i < n; i ++ ) {
add_eq( ans, mul( sum, C( i - 2, n - 3 ) ) );
}
printf( "%d\n", ans );
}
这好像是个原题
给你一个长度为 $n$ 的数列 $a$
如果 $a_i = a_{i + 1}$, 那么这两个数可以合并成 $a_i + 1$
问最小数列长度
$1 \leq a_i \leq 1000, 1 \leq n \leq 500$
区间 dp
设 $f_{i,j}$ 为 $i$ 到 $j$ 最小长度
$merged_{i,j}$ 为 $i$ 到 $j$ 合并出来的数字
然后就是标准板子了
// Woshiluo Luo<[email protected]>
// 2020/03/10 15:50:48
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 510;
template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }
int n;
int a[N];
int f[N][N], merged[N][N];
int main() {
#ifdef woshiluo
freopen( "e.in", "r", stdin );
freopen( "e.out", "w", stdout );
#endif
scanf( "%d", &n );
for( int i = 1; i <= n; i ++ ) {
scanf( "%d", &a[i] );
f[i][i] = 1;
merged[i][i] = a[i];
}
for( int len = 2; len <= n; len ++ ) {
for( int left = 1, rig = len; rig <= n; left ++, rig ++ ) {
f[left][rig] = rig - left + 1;
for( int mid = left; mid < rig; mid ++ ) {
int &f_left = f[left][mid], &f_rig = f[ mid + 1 ][rig],
&merge_left = merged[left][mid], &merge_rig = merged[ mid + 1 ][rig];
chk_Min( f[left][rig], f_left + f_rig );
if( f_left == 1 && f_rig == 1 && merge_left == merge_rig ) {
f[left][rig] = 1;
merged[left][rig] = merge_left + 1;
}
}
}
}
printf( "%d\n", f[1][n] );
}
题目连接: https://atcoder.jp/contests/agc034/tasks/agc034_f
道理我都懂,可是不看题解不到啊……
$$\newcommand \xor{\mathbin{\mathbf{xor}}}$$
给定一个数字 $n$
对于 $0 \cdots 2^n – 1$ 中每个数字都有一个 $a_i$
现在有一个数 $X$, 一开始为 $0$
每一次操作会随机选择一个数字 $i$ (其中 $0 \leq i \leq 2^n – 1$,选中 $i$ 的概率为 $\frac{a_i}{\sum a}$)
然后令 $X = X \oplus i$
问使 $X=i$ 的期望次数
先令
$$p_i=\frac{a_i}{\sum a}$$
令 $f_i$ 表示从 $i$ 到 $0$ 的期望次数,这和从 $0$ 到 $i$ 显然是一样的
接着有一个非常显然的式子
$$
\begin{aligned}
f_i &= f_{i \xor j} \times p_j + 1 ( i \neq 0 )\\
f_i – 1 &= f_{i \xor j } \times p_j ( i \neq 0 )
\end{aligned}
$$
然后我们就可以得到这个式子
$$ (f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \oplus (p_0, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\
= ( x, f_1 – 1, f_2 – 1, \cdots, f_{2^{n-1}} – 1, f_{2^n-1} – 1 ) $$
但是 $x$ 是未知的
注意到 $\sum p = 1$ 所以左边右边的和是相等的
所以
$$
\begin{aligned}
x &= \sum_{i=0}^{2^n-1} f_i – \sum_{i=1}^{2^n-1} f_i – 1 \\
&= f_0 + 2^n – 1
\end{aligned}
$$
$$
(f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \oplus (p_0, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\
= ( f_0 + 2^n – 1, f_1 – 1, f_2 – 1, \cdots, f_{2^{n-1}} – 1, f_{2^n-1} -1 )
$$
我们需要的使其中两个式子没有未知数
注意到如果使 $p_0 = p_0 – 1$
那么右边每一个数都会减去 $f_i$
即
$$
(f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \oplus (p_0 – 1, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\ = ( 2^n – 1, – 1, – 1, \cdots, -1, -1 )
$$
于是就可以放进 FWT 里直接做
然后你就发现这东西跑出来是错的……
为什么呢
令
$$
\begin{aligned}
\mathbf{P} & = (p_0 – 1, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\
\mathbf{A} & = ( 2^n – 1, – 1, – 1, \cdots, -1, -1 )
\end{aligned}
$$
因为 $\mathbf{P}$ 和 $\mathbf{A}$ FWT 后第一位都是 $0$
没有办法倒推出来 $f_0$
但是可以发现 $f$ 的值是可以平移的
即
$$ f_i + k = \sum_{i=0}^{2^n-1} p_j(f_{i \oplus j} + k) + 1 = \sum_{i=0}^{2^n-1} f_{i \oplus j} \times p_j + k + 1 $$
那么我们又知道 $f_i = 0$
所以将 $f$ 每一位都减去 $f_0$ 即可
#include <cstdio>
const int N = 1 << 20;
const int mod = 998244353;
int n;
int a[N], b[N], p[N], sum;
inline i
nt add( int a, int b ) { return ( a + b ) % mod; }
inline int mul( int a, int b ) { return ( 1LL * a * b ) % mod; }
inline void add_eq( int &a, int b ) { a = ( a + b ) % mod; }
inline void mul_eq( int &a, int b ) { a = ( 1LL * a * b ) % mod; }
int ksm( int a, int p ) {
int res = 1;
while( p ) {
if( p & 1 )
res = mul( res, a );
a = mul( a, a );
p >>= 1;
}
return res;
}
inline int inv( int a ) { return ksm( a, mod - 2 ); }
void XOR( int *f, int len, int x = 1 ) {
for( int o = 2, k = 1; o <= len; o <<= 1, k <<= 1 ) {
for( int i = 0; i < len; i += o ) {
for( int j = 0; j < k; j ++ ) {
add_eq( f[ i + j ], f[ i + j + k ] );
f[ i + j + k ] = add( f[ i + j ], add( - f[ i + j + k ], - f[ i + j + k ] ) );
mul_eq( f[ i + j ], x ); mul_eq( f[ i + j + k ], x );
}
}
}
}
int main() {
#ifdef woshiluo
freopen( "F.in", "r", stdin );
freopen( "F.out", "w", stdout );
#endif
scanf( "%d", &n );
n = 1 << n;
for( int i = 0; i < n; i ++ ) {
scanf( "%d", &a[i] );
sum += a[i];
b[i] = mod - 1;
}
sum = inv(sum);
for( int i = 0; i < n; i ++ ) {
p[i] = mul( a[i], sum );
}
b[0] = n - 1; p[0] -= 1;
XOR( b, n ); XOR( p, n );
for( int i = 0; i < n; i ++ ){
mul_eq( b[i], inv( p[i] ) );
}
XOR( b, n, inv(2) );
for( int i = 0; i < n; i ++ ) {
printf( "%d\n", ( ( add( b[i], -b[0] ) + mod ) % mod ) );
}
}
这几天写的题目并不算少
然而这道题是唯一一个看过去傻了的题目…
应该以前听过,题面看着眼熟
可是死活不会做…
基本上可以明确是到构造题
这里依赖两个性质:
树的中心的 $D$ 值是最小的
$$ D(fa) = D(x) – n + 2 \times size[i] $$
这个就是标准的树和树的重心的性质
可以推出来如果以重心做根,那么 $D$ 越大越在下面
所以尝试生成一颗以重心为根的树就行了
然后我发现我不会判否
结果发现最后生成出来树判断一次就行了…
读入 $D$ 排序
从大到小依此根据上面的式子找父亲
最后判断一下是否正确就可以了
// User: woshiluo
// Email: [email protected]
// Problem link: https://atcoder.jp/contests/arc103/tasks/arc103_d
// Comment:
// Why the problem id is 'F', but the link is 'arc103_d'
// Interesting
#include <cstdio>
#include <cstdlib>
#include <map>
#include <algorithm>
const int N = 1e5 + 1e3;
int n;
int size[N], father[N];
struct node{
int id;
long long d;
} a[N];
std::map<long long, int> mp;
void wrong() {
printf( "-1\n" );
exit(0);
}
bool cmp( node _a, node _b ) { return _a.d < _b.d; }
int main() {
#ifdef woshiluo
freopen( "F.in", "r", stdin );
freopen( "F.out", "w", stdout );
#endif
scanf( "%d", &n );
for( int i = 1; i <= n; i ++ ) {
scanf( "%lld", &a[i].d );
a[i].id = i;
size[i] = 1;
mp[ a[i].d ] = i;
}
std::sort( a + 1, a + n + 1, cmp );
int rt_d, rt;
rt = a[1].id; rt_d = a[1].d;
for( int i = n; i > 1; i -- ) {
int fa = mp[ a[i].d + 2LL * size[ a[i].id ] - n ];
if( fa == 0 ) {
wrong();
return 0;
}
size[fa] += size[ a[i].id ];
father[ a[i].id ] = fa;
}
for( int i = 1; i <= n; i ++ ) {
if( i == rt )
continue;
rt_d -= size[i];
}
if( rt_d != 0 )
wrong();
for( int i = 1; i <= n; i ++ ) {
if( i == rt )
continue;
printf( "%d %d\n", father[i], i );
}
}
裸的要死的数位 DP 题目
一眼看过去,平方和,有点懵
但是维护平方和也是老套路了
维护 $sum$, $cnt$, $pow_sum$ 就可以了
代码写的着急,会有一些不太好看
#include <cstdio>
const int mod = 1e9 + 7;
inline int add( int a, int b ) { return ( a + b ) % mod; }
inline int mul( int a, int b ) { return ( 1LL * a * b ) % mod; }
inline int power( int a ) { return mul( a, a ); }
int T, max_len;
long long left, rig;
int _count[110][2][10][10][10], _sum[110][2][10][10][10], _pow[110][2][10][10][10];
int max_count[110][2][10][10][10], max_sum[110][2][10][10][10], max_pow[110][2][10][10][10];
bool _vis[110][2][10][10][10];
int a[110];
inline int length( int cur, int len ) {
int res = cur;
while( len > 0 ) {
res = mul( 10, res );
len --;
}
return res;
}
struct node {
int len, cur, seven, rem, base_rem;
bool max;
int& count() {
if( max )
return max_count[len][seven][rem][cur][base_rem];
return _count[len][seven][rem][cur][base_rem];
}
int& sum() {
if( max )
return max_sum[len][seven][rem][cur][base_rem];
return _sum[len][seven][rem][cur][base_rem];
}
int& pow() {
if( max )
return max_pow[len][seven][rem][cur][base_rem];
return _pow[len][seven][rem][cur][base_rem];
}
bool& vis() {
return _vis[len][seven][rem][cur][base_rem];
}
void print() {
// printf( "%d %d %d %d %d %d\n", len, cur, seven, rem, base_rem, max );
// printf( "%d %d %d\n", count(), sum(), pow() );
}
};
int dfs( node cur ) {
if( ( ! cur.max ) && cur.vis() )
return cur.pow();
if( cur.len == 0 ) {
if( cur.seven || cur.rem == 0 || cur.base_rem == 0 ) {
cur.count() = cur.sum() = cur.pow() = 0;
return cur.pow();
}
cur.count() = 1; cur.sum() = cur.cur; cur.pow() = power( cur.cur );
return cur.pow();
}
int &cur_count = cur.count();
int &cur_sum = cur.sum();
int &cur_pow = cur.pow();
if( cur.max )
cur_count = cur_sum = cur_pow = 0;
for( int i = 0; i <= ( cur.max? a[cur.len]: 9 ); i ++ ) {
node nxt = cur;
nxt.len -= 1; nxt.seven = ( cur.seven || ( i == 7 ) ); nxt.cur = i; nxt.max = ( cur.max && i == a[cur.len] );
nxt.rem = ( cur.rem * 10 + i ) % 7; nxt.base_rem = ( cur.base_rem + i ) % 7;
dfs( nxt );
cur_count = add( cur_count, nxt.count() );
cur_sum = add( cur_sum, add( mul( length( cur.cur, cur.len ), nxt.count() ), nxt.sum() ) );
cur_pow = add( cur_pow, add( nxt.pow(), add( mul( nxt.count(), power( length( cur.cur, cur.len ) ) ),
mul( mul( 2, length( cur.cur, cur.len ) ), nxt.sum() ) ) ) );
}
if( cur.max == false )
cur.vis() = true;
cur.print();
return cur_pow;
}
int sum( long long _a ) {
if( _a == 0 )
return 0;
if( _a < 0 )
return 0;
max_len = 0;
while( _a ) {
a[ ++ max_len ] = _a % 10;
_a /= 10;
}
// printf( "ans: %d\n", dfs( (node){ max_len, 0, 0, 0, 0, true } ) );
return dfs( (node){ max_len, 0, 0, 0, 0, true } );
}
int main() {
#ifdef woshiluo
freopen( "hdu.4507.in", "r", stdin );
freopen( "hdu.4507.out", "w", stdout );
#endif
scanf( "%d", &T );
while( T -- ) {
scanf( "%lld%lld", &left, &rig );
if( left == 0 )
printf( "%d\n", ( sum(rig) + mod ) % mod );
else
printf( "%d\n", ( add( sum(rig), - sum( left - 1 ) ) + mod ) % mod );
}
}
树形 DP 要么模板题,要么神仙题
这个题就属于不太正常的
$f_{i,j}$
其中 $i$ 代表当前节点
$j$ 代表子树中选择 $j$ 个黑点,所能对答案产生的贡献
有了这个思路,后面的东西就是标准的树上背包了
#include <cstdio>
#include <cstring>
inline int Min(int a, int b) { return a < b? a : b; }
inline long long Max(long long a, long long b) { return a > b? a : b; }
const int N = 2100;
int n, m;
int size[N];
// Edge Start
struct edge {
int to, val, next;
} e[N << 1];
int ehead[N], ecnt;
inline void add_edge(int now, int to, int val) {
ecnt ++;
e[ecnt].to = to;
e[ecnt].val = val;
e[ecnt].next = ehead[now];
ehead[now] = ecnt;
}
// Edge End
// DP Start
long long f[N][N];
void dp(int now, int la) {
size[now] = 1;
f[now][0] = f[now][1] = 0;
for(int i = ehead[now]; i; i = e[i].next) {
if( e[i].to == la )
continue;
dp(e[i].to, now);
size[now] += size[ e[i].to ];
for(int j = Min(size[now], m); j >= 0; j--) {
for(int k = 0; k <= Min(size[ e[i].to ], j); k++) {
if( f[now][ j - k ] == -1 )
continue;
long long val = 1LL * ( 1LL * k * ( m - k ) + 1LL * ( size[ e[i].to ] - k ) * ( n - m - size[ e[i].to ] + k ) ) * e[i].val;
f[now][j] = Max( f[now][j], f[now][j - k] + f[ e[i].to ][k] + val);
}
}
}
}
// DP End
int main() {
#ifdef woshiluo
freopen("luogu.3177.in", "r", stdin);
freopen("luogu.3177.out", "w", stdout);
#endif
scanf("%d%d", &n, &m);
for(int i = 1, u, v, w; i < n; i++) {
scanf("%d%d%d", &u, &v, &w);
add_edge(u, v, w);
add_edge(v, u, w);
}
memset(f, -1, sizeof(f));
dp(1, 0);
printf("%lld", f[1][m]);
fclose(stdin);
fclose(stdout);
}
晚上十一点半的机房,Woshiluo 正悠闲地考虑着第二天该做的题目
说着他一瞬想起来自己似乎报名过 PKUWC
「过去几年都没有人通过的,今年夏令营整个新疆都没过,果然还是白忙活吧」
话虽这么说,Woshiluo 还是打开了报名网站
「喵喵喵,过了?」
在经过严密的安检过后
我终于到达了乌鲁木齐站三层
比较有意思是,我的检票口貌似是 A1
然而 A1 前面的人都已经坐满了
我走到夹层,和绝大多数高铁站一样,这个地方是用来骗钱的
不过也就意味着,这里的人会少一点
稍作调整之后,车站广播也响起来了
随着缓慢的人群,我通过了检票口,到达了火车上
乌鲁木齐铁路局所销售物品的昂贵已经是有很久历史的事情了
所以如果上车的时候犯懒没有买足吃的东西的话
可以到大站下去购买以尽可能的减少损失
这趟列车绝大部分地区都是没有信号的地方
所以基于手机的娱乐基本上是没有了
Kindel 和随身携带的纸制书可能是最有效的消磨时间方式
所以我在火车上过了一遍绿书,并读完了「樱花庄的宠物女孩」的本篇及番外
我的上铺和下铺都是年轻人,一个考研,一个去内地学习技术
总而言之,大家也都有自己的前途
在早上十点,火车到达了北京西站
我从地铁口出来,联系上了 sbr,我从北京西站到国家图书馆再换乘到海淀黄庄
两个人见面稍微寒暄了一小阵,就到星巴克里谈人生
到了快要报道的时间,我们从海淀黄庄坐到北京大学东门 D 出口,从 D 口出来后约 5min 就可以到达报道的地方
我前面比没有几个人,进去之后签到的一栏旁边还有一个 是否使用 NOI Linux
看到这一栏时,就体会到了 PKU 的良心,更不要提后面还有 PKU 食堂 150 CNY 的饭卡
和 sbr 打车到国贸大厦,惊奇的发下自己把一些东西落在了报到处…
算了不重要
然后在和 sbr 吃完午饭后,我坐地铁从国贸到东大桥
经过 5min 的第六感导航,我顺利的到达了 Animate
这里的 Animate 和广东店装修风格好像啊
不过比广东店大了不少
时间超级紧迫,因为很明显,我需要在报道时间结束前回到报道处,拿回自己的东西
所以我就一目十行,随便扫了点 LoveLive! 和 碧蓝航线 的东西就飞速离开了
不过最意外的收获就是搞到了一本 「LoveLive! School idol diary」还是日文原版的
然后一路 Rush 到北京大学的报道处,偷偷的拿到了自己的东西
接着到了自己的酒店,真的离 PKU 好远啊…
稍微整理了一下在 Animate 的战果后跑去和 杨老师 — 我们的教练 会面
然后发现自己这边里所有地铁站都好远啊…
选了个最近的地铁站 — 上地站,然后惊奇的发现 13 号线几乎全是在地表的,我一开始以为这是轻轨,后来发现这东西叫城铁
从上地站出发,换乘两次到鼓楼,然后杨老师告诉我她到雍和宫了
于是从鼓楼出发,换乘一次到雍和宫
后面和老师吃了顿饭,然后和地铁关闭时间赛跑,换乘两次回到上地站
待我再回到酒店的时候,我人已经完全坏掉了
早上在酒店滴滴了 10min +,终与打到了车…
匆匆忙忙,到达开幕式所在场所
认识了几位大佬
开幕式大约介绍了一下北大,然后就散了
然后发现,没有地方休息的说
在经历了一阵混乱后,最后反正是在中关新园定了一间房
嘛,虽然贵的要死,不过明天也算是有地方休息了
反正今天是没有时间了
和教练一起在食堂吃了顿饭
在吃完 PKU 食堂的饭后,真的让人想上 PKU (不是
食堂饭又便宜又好吃,感谢 PKU 给的饭卡
然后跑到考场,Windows 考生使用 Windows 10,具体配置也不清楚
NOI Linux 考生使用的是 Dell 的笔记本,Intel i7,16G DDR4
多余的也没看,毕竟体验很优秀
T1
T2
T3
告辞 28pts 跑路
在 T1 莽的太久了, T2 T3 好多暴力没拿
中关新园离 pku 是真的近(
回去没有什么事情,就一直在颓废
据说面试还有紧张到说不出话的
基本上面试文的问题都挺普通的
面试的时候会有一个预定的时间表
我想着我还没到时间,就去食堂买了个面包当早餐
结果回来发现已经到下一个了
无限尴尬
不过还好,面试老师并没有说什么
和一个天津大佬面基成功w
T1
T2
T3
最后 100 + 32 + 0 = 132pts
还算可以,基本上算是我的最大能力了(
回到酒店后,想起今天是冬至,便外卖了份饺子
然而是真的贵
吃完虽然没有吃饱,不过就这样子吧
然后颓废
早早的就起来了
和 lk 爷爷面基,一起在楼下的包子铺吃了招行
两个人扯皮了好久
Orz lk 太强了
后来两个人打车去 PKU,中间 LK 在 THU 下了
我到了之后刚好开幕
我们教练家女儿生病了,于是我又双是一个人了
一开始大概就是领导讲话
然后就开始讲题
讲下来感觉自己还是很有希望 200 + 的
不过 Day 1 的时候智商下线了w
题倒是都还好,除了 Day 1 T3 完全没搞懂
剩下的都是能听懂但是不会写的(
然后颁奖
我觉得和我没什么关系,就把衣服拉链来开了,毕竟很热
然后惊奇的发现有个三等奖,结果上去的时候拉链没拉上www
后面发现闭幕式结束才 15 点
直接冲出去坐地铁到搜秀城w
然后再采购
然后就是回宾馆睡觉
晚上和 sbr 再面基
上午和 Galaxy 见面
(或许是我这次唯一一个面基的成年人?)
然后带我逛了下 THU,顺便在 THU 吃了顿饭
和他聊了一阵子天,受益匪浅
正准备去机场
然后飞机延误了
意外之喜
于是再去了趟 Animate,好好逛了一把
发现了好多之前没有发现的东西
大收获的说
飞机又双延误了
告辞,火车真香
今年感觉相比较以往还是难了一些的
不过没有大家说的那么夸张倒是真的
不过自己貌似真的变菜了
笔者考完 CSP 就会初中部回了一天
然后周二病到周五
一直在不发烧和低烧徘徊
但是一直咳嗽
咳得人 头疼 + 胸口疼 + 嗓子疼
新疆地邪啊
今年七十中并不给试机子(注:也有可能是 CCF/科协 不让)
罢了,也没什么看头,每年 11 月都会在七十中冻一下
中午大家一起吃饭
虽然想学习,最后还是放弃了
晚上翻来覆去睡不着,迷迷糊糊最后还是睡了
早上一开门,哇,下雪了
无论这个比赛怎么改名下雪还是避免不了的
在寒冷的凌晨,我们又拿起武器,向着熟悉的战场再次走去
T3 是个啥啊?
一中学长们的校车因为各种各样的原因,8:25 才到
遇见了不少以前的熟人,SpinMry,dyf 大家好久不见了,一起加油吧!
匆匆忙忙赶进考场,熟练的敲下 .vimrc
,慌的一批
到了时间,准点发题
一直很想问都 9102 年,新疆为什么还是不用压缩包加密这种可靠性高的多的方法
非要用电子教师软件传输,然后再匆匆忙忙断网
罢了,有人愿意举办就不错了
unsigned long long
,出题人太毒瘤了吧114514
太臭了,一跑,爆栈了,手写递归去了 考完心态小崩,找 dyf 一起快乐
两个人一起吃了饭,然后开始互相吐槽各自的学校
聊完天,两个人有一起走到七十中,路上还在吐槽某大佬的的迷路属性,结果到了七十中校门口就碰到了某大佬
先在二楼和 dyf 与 caq 一起聊天 CSP 本质面基大会无疑
然后快考试了才去了三楼,运气可以抽到了比较暖和的位置
一出考场发现 2 道原题过分了啊
T3 我竟然做过,傻了
又见熟人,不过因为 Day1 的心态,所以就没有过多闲聊
遇到郭老师,问他 Day 1 爆栈的问题
满口都是什么
「机子环境不重要的啊」
「全国只有两个省份提供了 NOI Linux,还都是虚拟机,别的都是 Windows 」
我看加个「七十中断电和七十中无关」都可以郭老师三连了
顺带一提,Day2 T2 内存 1GB 我们考试环境总共就 1GB 内存,可把我给整乐了
算了,XJ 这个占全国 $\frac{1}{6}$ 领土的小省份还是不要想别的了吧
随便在一中食堂买了桶泡面
然后就会机房和大家一起聊天
高二的学长们很大一部分都要退役了,说不好真的是最后一次见了…
祝各位安好
一点 CSP 过后的小记
选手程序出了
找了几个数据在 NOI Linux 上测试了一下
顺带一提,是远程测试,是真的延迟高…
最后基本上都是
$100 + 55 + 0 + 0 + 64 + 40 = 259$
也不知道官方数据怎么样…
斗胆看了下 Day1 T2
发现自己把
st[ st_cnt ]
写成了
st_cnt
离谱
正在去市一中的路上,突然看到有个群里说成绩出了
CCF 又写 Bug了(当然不排除故意的可能性)
一路狂奔跑到机房
拿到数据正准备测试,结果机房 DNS 锅了
看了一下发现 php-fpm
服务挂了
修复了之后开始评测,然后在大屏上直播
感觉还可以,评测期间基本上就是在骂测了 1min + 但是却 0 pts 的憨憨
想了想,还是有想停课冲冬令营的想法
稍作思考也许就会出结果了吧…
回去文化课实际上感觉还可以,基本上也都听得懂
勉强在班里还算可以,不过在这个学校里想要上一中必须得很靠前啊…
今年可能比较去年可能会更加迷茫一些
去年的问题有一条今年还有的
除此之外,能察觉到的主观问题还有
以下是一些客观原因
顺带一提,我和同省大爷 StudyingFather 的意见一样
个人感觉我们今年的培训题目还是太偏了,考试虽然一直都在考,但是留于改题与总结的时间并不算长
不过我们也确实做不到跟上出题趋势这个大方向
希望我们会走的更好吧
「Jump up HIGH!!」
– Aqours
今年已经是我第三年学习 Oi 了
也许自己真的在实现最初的梦想
当时刚学 Oi 的时候,就一直在想,要是我能拿到 XJ 的第二块 Au 就好了
后来目标渐渐变了,变成很多奇奇怪怪的东西
回过头来,却发现自己也无意识中接近了最初的目标
不过还是很遥远就是了
不过人好歹还是要有梦想的,毕竟自己文化课也不算优秀
所以说,就当是有梦想了吧
虽然自己菜的真实
「私たち、 輝きたい!」
「LoveLive!SunShine!!」
Woshiluo
2019/12/04
乌鲁木齐市第一中学 小机房
因为要帮同学们复习的时候选题,选到堆的时候问了问 StudyingFather 的意见,他给我推荐了这个题目
超级感谢推荐,这个题目真是太有意思了(
Continue reading “Luogu P3871 [TJOI2010]中位数”