zoukankan      html  css  js  c++  java
  • CodeCraft-21 and Codeforces Round #711 (Div. 2)

    A. GCD Sum

    题目描述

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

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

    (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);
    }
    
  • 相关阅读:
    课程总结
    第十四周课程总结&实验报告
    第十三周课程总结
    第十二周课程总结
    第十一周课程总结
    第十周课程总结
    第七次java实验报告
    第六次java实验报告
    第五次java实验报告
    第四次java实验报告
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14729256.html
Copyright © 2011-2022 走看看