题解 P7841 【「C.E.L.U-03」100%不公平的游戏】
VinstaG173 · · 题解
是真的没有代码能力了/ll
一道大毒瘤题/baojin
SG 定理 + 换根 dp。
首先想到先手标记了一条边后,问题转化成了两个子游戏的和,每个游戏都是一棵有根树,要求变成在有根树中标记边且这些边都要在一条以根为一个端点的链上。
为什么要转化成这样的游戏呢?这是由于在这个游戏中,操作一步转化出的子游戏形态与之相似。以下用
如在以下一棵树中(根为
若我们标记了
显然此时接下来标记的边只可能在
而链的 SG 值为链长
得到。
故由以上分析,任意选定根后可得
其中
于是我们要关注的就是
且有
在这个转移方程的基础上进行换根 dp 即可。用 bitset 维护集合,SG 值最大可能为
具体实现中,我们对每个点用两个 bitset 维护它父亲的儿子中比它先遍历到的点的 所以调死我了。
bitset 库函数 _Find_first 可以快速找到最低位的
不要像我一样只用脑子想,最好用下草稿纸,不然就像我一样想错了都不知道
Code:
#include<bitset>
#include<cstdio>
#include<vector>
#define rg register
using std::bitset;
using std::vector;
int sg[500003];
int snm[500003];
vector<int> e[500003];
bitset<40> S[500003][2],T;
bitset<40> lS[500003][2],rS[500003][2];
inline void add(int x,int y){e[x].push_back(y),e[y].push_back(x);}
void dfs1(int u,int f)
{
snm[u]=e[u].size();
bitset<40> tlS0,trS0;
bitset<40> tlS1,trS1;
tlS0.reset(),trS0.reset();
tlS1.reset(),trS1.reset();
for(rg int i=1,v;i<snm[u];++i)
{
v=e[u][i];if(v==f)continue;dfs1(v,u);
lS[v][0]=tlS0,tlS0|=S[v][1];
lS[v][1]=tlS1,tlS1|=S[v][0];
}
for(rg int i=1,v;i<snm[u];++i)
{
v=e[u][snm[u]-i];if(v==f)continue;
rS[v][0]=trS0,trS0|=S[v][1];
rS[v][1]=trS1,trS1|=S[v][0];
}
sg[u]=(~tlS0)._Find_first(),S[u][0]=tlS0,S[u][1]=tlS1;
S[u][0].set(sg[u]^1),S[u][1].set(sg[u]);
}
int dfs2(int u,int f,bitset<40> tS0,bitset<40> tS1)
{
if(f)
{
int tsg=(~tS0)._Find_first();
if(tsg==sg[u])return 1;
tS0.set(tsg^1),tS1.set(tsg);
}
for(rg int i=1,v;i<snm[u];++i)
{
v=e[u][i];if(v==f)continue;
if(dfs2(v,u,tS1|lS[v][0]|rS[v][0],tS0|lS[v][1]|rS[v][1]))return 1;
}
return 0;
}
inline void init(int n)
{
T.reset();
for(rg int i=1;i<=n;++i)
{
sg[i]=snm[i]=0;
e[i].clear(),e[i].push_back(0);
S[i][0].reset(),S[i][1].reset();
lS[i][0].reset(),rS[i][0].reset();
lS[i][1].reset(),rS[i][1].reset();
}
}
int t,n,u,v;
int main()
{
scanf(" %d",&t);
while(t--)
{
scanf(" %d",&n);init(n);
for(rg int i=1;i<n;++i)scanf(" %d %d",&u,&v),add(u,v);
dfs1(1,0);puts((dfs2(1,0,T,T))?"Play now":"Restart");
}
return 0;
}