A. GCD Sum
题目描述
设 (s(i)) 为 (i) 的数位和,求第一个 (geq n) 满足下式的 (x):
(1leq nleq 10^{18}),数据组数不超过 (10^4) 组
解法
一开始没想法,然后打了个爆搜过掉了。
根据小学奥数可知若 (x) 为 (3) 的倍数则 (gcd(x,s(x))=3),所以就算是爆搜也只需要搜三次。
B. Box Fitting
题目描述
解法
又猜了结论,就是能填大的就暴力填大的。
证明就考虑如果某次放弃了填大的机会,那么我们肯定会填小的,但是若干个小的一定等价于一个大的,因为小的都是大的因数。那么既然是等价的填大的可以让以后的选择更灵活,所以贪心成立。
E. Two Houses
题目描述
解法
再次猜结论,就是入度小的点一定能到达入度大的点,然后随便搞一下就 ( t A) 了。
证明:设两个点的入度分别是 (a,b(aleq b)),那么 (a) 连出去的边有 (n-1-a) 条,(b) 连进来的边有 (b) 条,只考虑这些边构成的图,如果没有点重复被边连那么总点数是 (n-1-a+b+2=n+1+(b-a)geq n+1),这说明一定有点重复,那么 (a) 对应的点可以到达 (b) 对应的点。
F. Christmas Game
题目描述
解法
首先考虑对于一个固定的根怎么做?发现模 (k) 不同的深度是互不干扰的,所以可以分开来考虑,因为是博弈问题所以可以使用 ( t sg) 函数把每个子问题合并起来,现在考虑子问题即可。
发现子问题其实就是阶梯博弈:有长度为 (n) 的阶梯,每次可以把某个阶梯上的石子移动到上一个阶梯,如果移动到地面则不能移动,不能操作者输。那么这个游戏的 ( t sg) 函数就是奇数位置上石子的异或和。
证明:我们找出一种状态,让某个人可以维持这种状态,直到另一个人输掉。如果先手遇到的状态是奇数位置上石子异或和为 (0),如果先手操作奇数位置后手可以让奇数位置异或和恢复 (0)((color{red}tag)),如果先手操作偶数位置那么后手把等量的石子移回奇数位置,那么后手必胜。如果先手遇到的状态是奇数位置上石子异或和非 (0),那么先手可以一步操作让奇数位置异或和变成 (0),先手必胜((color{red}tag))。
打了 (color{red}tag) 可能是你要把下面的东西读完才能搞清楚的,下面证明 ( t sg) 函数的子问题组合定理:
如果一个游戏由很多独立子游戏构成,那么 ( t sg) 值为所有子游戏的 ( t sg) 值的异或:
[f(x)=igoplus_{i=1}^nf_i(x_i) ]证明考虑归纳法,最终的状态(先手输)一定满足这个等式,我们假设某个状态的后继状态满足这个定理,那么只需要证明这个状态也满足就行了,设 (b=igoplus_{i=1}^n f_i(x_i))。
假设后继让子游戏 (j) 的状态 (x_j) 变成了 (x_j'),它的 ( t sg) 函数就是 (boplus f_j(x_j)oplus f_j(x_j'))
根据 ( t mex) 的定义,我们只需要证明两点:
- (forall ain[0,b)),存在 (x_j') 满足 (boplus f_j(x_j)oplus f_j(x_j')=a),我们可以取最大的 (f_j(x_j)),那么这个子问题的后继满足 (f(x_j')) 能够取遍 ([0,f_j(x_j))),我们可以让 (b) 的某一个位由 (1) 变 (0),那么较低的那些位就可以随便取,也就是说可以构造出任意数。这个证明方法可以说明 (color{red} tag) 的结论,我们取最大的奇数位置的石子操作即可。
- (forall x_j',boplus f_j(x_j)oplus f_j(x_j') ot=b),这个东西在 (f_j(x_j)=f_j(x_j')) 的时候不成立,但是根据 ( t mex) 的定义上述情况是不会发生的。
所以 ( t mex) 和 (oplus) 这两个奇怪的东西就产生了神秘的 ( t sg) 函数!
知道了上面这些东西之后就好做了,因为 (kleq20) 所以可以暴力换根,记 (f[u][i]) 表示以 (u) 为根模 (2k) 为 (i) 的深度的石子异或和即可。
#include <cstdio>
const int M = 100005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,k,tot,a[M],f[M],g[M],ans[M],dp[M][45];
struct edge
{
int v,next;
}e[2*M];
void pre(int u,int fa)
{
dp[u][0]^=a[u];
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
pre(v,u);
for(int j=0;j<2*k;j++)
dp[u][(j+1)%(2*k)]^=dp[v][j];
}
}
void dfs(int u,int fa)
{
for(int i=k;i<2*k;i++) ans[u]^=dp[u][i];
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
for(int j=0;j<2*k;j++)
g[(j+1)%(2*k)]=dp[u][(j+1)%(2*k)]^dp[v][j];
for(int j=0;j<2*k;j++)
dp[v][(j+1)%(2*k)]^=g[j];
dfs(v,u);
}
}
signed main()
{
n=read();k=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e[++tot]=edge{v,f[u]},f[u]=tot;
e[++tot]=edge{u,f[v]},f[v]=tot;
}
for(int i=1;i<=n;i++)
a[i]=read();
pre(1,0);
dfs(1,0);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]!=0);
}