题解:【CF1610I】 Mashtali vs AtCoder

题目链接

\(k = 1\) 开始考虑,即为这道题

记点 \(u\) 的 SG 函数为 \(f_u\)\(v\) 为点 \(u\) 的儿子,则有 \(f_u = \oplus (f_v + 1)\)

首先每次操作是从根节点的一棵子树中选一条边删掉,这说明了根节点每个儿子的游戏是独立的,所以考虑使用 nim 和求解。假如说根节点有 \(k\) 个子树,那么将根拆成 \(k\) 个点,每个点下面连原先的一个儿子(即每个节点下面挂一个子树),根据 nim 和的结论,我们要求的答案即为 \(k\) 棵新树的 SG 函数异或起来。问题变成了在一棵树顶上新连出来一条边,该树的 SG 函数值会如何变化。

pCAcX3d.png

根据题目中任何时刻都可以选择当前局面中一条边 \((u,v)\) 切掉,画出博弈对应的有向图,发现这相当于给每一个节点都增加了一个后继的超级汇点,而且显然这个超级汇点为必败态,SG 函数为 \(0\)。原先图中 SG 函数为 \(1\) 的点变成了 \(2\),原来为 \(2\) 的点变成了 \(3\),以此类推,这个操作使得局面中所有状态的 SG 函数都偏移了 \(1\),于是可以归纳出上面的结论,一棵树的 SG 函数是所有子树的 SG 函数值 \(+1\) 的异或和。

接下来考虑加入更多的关键点,钦定依然以 \(1\) 号点为根,我们可以将整棵树划分成若干部分:

  1. 如果对于节点 \(u\)\(u\) 的子树内不存在关键点,但是对于它的父亲 \(v\)\(v\) 的子树内存在关键点。那么将 \(u\) 和其子树拉出来,再加上 \(v\) 点这是一个独立的游戏,答案为上面得到的结论:游戏的 SG 函数为这棵子树的 SG 函数值 \(+1\)
  2. 刨除所有的第一种部分后,剩下的一坨东西满足度为 \(1\) 的点都是关键点。不妨将其看做无根树,接下来我们需要解决这棵树的 SG 函数。

这棵无根树的 SG 函数为树的边数 \(\mod 2\)

首先有引理,对于存在这种关键点的树,\(\text{SG 函数} \equiv \text{树的边数} (\mod 2)\)。如果只删除一条边并不会导致删除,所以随便删掉一条边后剩下的两个子游戏的 SG 异或和 \(\not\equiv {边数} (\mod 2)\),据此这棵树 SG \(\leq \text{边数} \mod 2\)。对于边数为偶数的情况已经得解,至于边数为奇数的还不是特别理解,这里引用一下 ZJ 队长的证明:

任取一个度为 \(1\) 的节点和距其最近的度不为 \(2\) 的节点(对于度为 \(2\) 的节点,总有唯一的出边,因此该点唯一),并在两者的路径上去边。假设路径长度为 \(l\),将 \(l\) 分为 \(x\)\(y\) 两段(其中 \(x + y = l − 1\)),这两段在新问题中都将作为第 \(1\) 种情况,而去掉后又是一个第 \(2\) 种情况,可以由归纳假设确定其 SG 值。

依然根据 nim 和,最后的答案就是各个部分的 SG 异或起来。具体可以如何实现呢?首先依然要做一遍以 \(1\) 为根的树形 DP,预处理出初始的每个点的 SG 函数以及父节点关系。考虑一次添加关键点 \(u\),有多少节点的贡献从第一种游戏变到了第二种游戏,显然为 \(u\) 的根链上之前不是第二种贡献的节点。记现在要修改节点 \(i\) 的贡献,根据异或的自反性,先异或上 \(SG_i + 1\) 消除贡献,然后再异或上 \(SG_i\) 作为新的贡献,最后处理加入到无根树中对无根树边数的变化,可以直接多异或一个 \(1\),综上式子就是 \(\oplus (SG_i + 1) \oplus SG_i \oplus 1\)。每次加入关键点可以直接暴力跳父亲,因为每个节点只会转变一次贡献,所以均摊下来时间复杂度是 \(\mathcal O(n)\) 的。

#include<bits/stdc++.h>
#define ld long double
#define ui unsigned int
#define ull unsigned long long
#define int long long
#define eb emplace_back
#define pb pop_back
#define ins insert
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define power(x) ((x)*(x))
#define gcd(x,y) (__gcd((x),(y)))
#define lcm(x,y) ((x)*(y)/gcd((x),(y)))
#define lg(x,y)  (__lg((x),(y)))
using namespace std;

namespace FastIO
{
	template<typename T=int> inline T read()
	{
	    T s=0,w=1; char c=getchar();
	    while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	    while(isdigit(c)) s=(s<<1)+(s<<3)+(c^48),c=getchar();
	    return s*w;
	}
	template<typename T> inline void read(T &s)
	{
		s=0; int w=1; char c=getchar();
		while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	    while(isdigit(c)) s=(s<<1)+(s<<3)+(c^48),c=getchar();
	    s=s*w;
	}
	template<typename T,typename... Args> inline void read(T &x,Args &...args)
	{
		read(x),read(args...);
	}
	template<typename T> inline void write(T x,char ch)
	{
	    if(x<0) x=-x,putchar('-');
	    static char stk[25]; int top=0;
	    do {stk[top++]=x%10+'0',x/=10;} while(x);
	    while(top) putchar(stk[--top]);
	    putchar(ch);
	    return;
	}
}
using namespace FastIO;

namespace MTool
{
	template<typename T> inline void Swp(T &a,T &b) {T t=a;a=b;b=t;}
    template<typename T> inline void cmax(T &a,T b) {a=a>b?a:b;}
    template<typename T> inline void cmin(T &a,T b) {a=a<b?a:b;}
    struct ModTool
    {
    	#define TA template<typename T,typename... Args>
    	#define TT template<typename T>
    	int Mod;
    	ModTool(int MOD=998244353):Mod(MOD){}
    	void ChangeMod(int MOD) {Mod=MOD;}
    	TT inline void Madd(T &a,T b) {a=a+b>Mod?a+b-Mod:a+b;}
    	TT inline void Mdel(T &a,T b) {a=a-b<0?a-b+Mod:a-b;}
    	TT inline void Mmul(T &a,T b) {a=a*b%Mod;}
    	TT inline void Mmod(T a) {a=(a%Mod+Mod)%Mod;}
    	TT inline T Cadd(T a,T b) {return a+b>=Mod?a+b-Mod:a+b;}
    	TT inline T Cdel(T a,T b) {return a-b<0?a-b+Mod:a-b;}
    	TT inline T Cmul(T a,T b) {return a*b%Mod;}
    	TT inline T Cmod(T a) {return (a%Mod+Mod)%Mod;}
    	TA inline void Madd(T &a,T b,Args... args) {Madd(a,Cadd(b,args...));}
    	TA inline void Mdel(T &a,T b,Args... args) {Mdel(a,Cadd(b,args...));}
    	TA inline void Mmul(T &a,T b,Args... args) {Mmul(a,Cmul(b,args...));}
    	TA inline T Cadd(T a,T b,Args... args) {return Cadd(Cadd(a,b),args...);}
    	TA inline T Cdel(T a,T b,Args... args) {return Cdel(Cdel(a,b),args...);}
    	TA inline T Cmul(T a,T b,Args... args) {return Cmul(Cmul(a,b),args...);}
    	TT inline T qpow(T a,T b) {int res=1; while(b) {if(b&1) Mmul(res,a); Mmul(a,a); b>>=1;} return res;}
    	TT inline T qmul(T a,T b) {int res=0; while(b) {if(b&1) Madd(res,a); Madd(a,a); b>>=1;} return res;}
    	TT inline T spow(T a,T b) {int res=1; while(b) {if(b&1) res=qmul(res,a); a=qmul(a,a); b>>=1;} return res;}
    	private:TT inline void exgcd(T A,T B,T &X,T &Y) {if(!B) return X=1,Y=0,void(); exgcd(B,A%B,Y,X),Y-=X*(A/B);}
    	public:TT inline T Ginv(T x) {T A=0,B=0; exgcd(x,Mod,A,B); return Cmod(A);}
    	#undef TT
    	#undef TA
	};
}
using namespace MTool;

inline void file()
{
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
    return;
}

bool Mbe;

namespace LgxTpre
{
    static const int MAX=300010;
    static const int inf=2147483647;
    static const int INF=4557430888798830399;
    static const int mod=1e9+7;
    static const int bas=131;
	
	int n,x,y,tmp;
	vector<int> G[MAX];
	int sg[MAX],fa[MAX],vis[MAX];
	
	void dfs(int now,int father)
	{
		fa[now]=father;
		for(auto to:G[now]) if(to!=father) dfs(to,now),sg[now]^=sg[to]+1;
	}
	
    inline void lmy_forever()
    {
    	read(n);
    	for(int i=1;i<n;++i) read(x,y),G[x].eb(y),G[y].eb(x);
    	dfs(1,0),tmp=sg[1],putchar(tmp?'1':'2');
    	for(int i=2;i<=n;++i) 
    	{
    		for(int now=i;now!=1&&!vis[now];now=fa[now]) vis[now]=1,tmp^=sg[now]^(sg[now]+1)^1;
    		putchar(tmp?'1':'2');
		}
		puts("");
    	return;
    }
}

bool Med;

signed main()
{
//  file();
    fprintf(stderr,"%.3lf MB\n",abs(&Med-&Mbe)/1048576.0);
    int Tbe=clock();
    LgxTpre::lmy_forever();
    int Ted=clock();
    cerr<<1e3*(Ted-Tbe)/CLOCKS_PER_SEC<<" ms\n";
    return (0-0);
}
posted @ 2023-06-09 15:01  LgxTpre  阅读(81)  评论(0编辑  收藏  举报