CodeCraft-21 and Codeforces Round #711 (Div. 2)

A. GCD Sum

题目描述

\(s(i)\)\(i\) 的数位和,求第一个 \(\geq n\) 满足下式的 \(x\)

\[\gcd(x,s(x))>1 \]

\(1\leq n\leq 10^{18}\),数据组数不超过 \(10^4\)

解法

一开始没想法,然后打了个爆搜过掉了。

根据小学奥数可知若 \(x\)\(3\) 的倍数则 \(\gcd(x,s(x))=3\),所以就算是爆搜也只需要搜三次。

B. Box Fitting

题目描述

点此看题

解法

又猜了结论,就是能填大的就暴力填大的。

证明就考虑如果某次放弃了填大的机会,那么我们肯定会填小的,但是若干个小的一定等价于一个大的,因为小的都是大的因数。那么既然是等价的填大的可以让以后的选择更灵活,所以贪心成立。

E. Two Houses

题目描述

点此看题

解法

再次猜结论,就是入度小的点一定能到达入度大的点,然后随便搞一下就 \(\tt A\) 了。

证明:设两个点的入度分别是 \(a,b(a\leq 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\) 不同的深度是互不干扰的,所以可以分开来考虑,因为是博弈问题所以可以使用 \(\tt sg\) 函数把每个子问题合并起来,现在考虑子问题即可。

发现子问题其实就是阶梯博弈:有长度为 \(n\) 的阶梯,每次可以把某个阶梯上的石子移动到上一个阶梯,如果移动到地面则不能移动,不能操作者输。那么这个游戏的 \(\tt sg\) 函数就是奇数位置上石子的异或和

证明:我们找出一种状态,让某个人可以维持这种状态,直到另一个人输掉。如果先手遇到的状态是奇数位置上石子异或和为 \(0\),如果先手操作奇数位置后手可以让奇数位置异或和恢复 \(0\)\(\color{red}tag\)),如果先手操作偶数位置那么后手把等量的石子移回奇数位置,那么后手必胜。如果先手遇到的状态是奇数位置上石子异或和非 \(0\),那么先手可以一步操作让奇数位置异或和变成 \(0\),先手必胜(\(\color{red}tag\))。

打了 \(\color{red}tag\) 可能是你要把下面的东西读完才能搞清楚的,下面证明 \(\tt sg\) 函数的子问题组合定理:

如果一个游戏由很多独立子游戏构成,那么 \(\tt sg\) 值为所有子游戏的 \(\tt sg\) 值的异或:

\[f(x)=\bigoplus_{i=1}^nf_i(x_i) \]

证明考虑归纳法,最终的状态(先手输)一定满足这个等式,我们假设某个状态的后继状态满足这个定理,那么只需要证明这个状态也满足就行了,设 \(b=\bigoplus_{i=1}^n f_i(x_i)\)

假设后继让子游戏 \(j\) 的状态 \(x_j\) 变成了 \(x_j'\),它的 \(\tt sg\) 函数就是 \(b\oplus f_j(x_j)\oplus f_j(x_j')\)

根据 \(\tt mex\) 的定义,我们只需要证明两点:

  • \(\forall a\in[0,b)\),存在 \(x_j'\) 满足 \(b\oplus 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',b\oplus f_j(x_j)\oplus f_j(x_j')\not=b\),这个东西在 \(f_j(x_j)=f_j(x_j')\) 的时候不成立,但是根据 \(\tt mex\) 的定义上述情况是不会发生的。

所以 \(\tt mex\)\(\oplus\) 这两个奇怪的东西就产生了神秘的 \(\tt sg\) 函数!

知道了上面这些东西之后就好做了,因为 \(k\leq20\) 所以可以暴力换根,记 \(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);
}
posted @ 2021-05-04 12:25  C202044zxy  阅读(69)  评论(0编辑  收藏  举报