#18009. Not a Work of Idol 2 题解
题目链接:#18009. Not a Work of Idol 2
很厉害!
考虑把值按照 $ 1, n, 2, n-1, \cdots $ 填进去,可以把原问题转换成一个删点顺序的问题:称偶数轮的数为大数,奇数轮的数为小数,显然在大数轮选择删掉的点必须是孤立点(因为与其相连的点都应该是在它删掉之前的小数(删掉的点),并且每次删掉的是孤立点可以满足不会出现两个大数相连的情况)。
显然对于最后两个点的删去方式总是对称的,因此答案肯定是 2 的倍数,因此在除了最后两个点的时候全程不应该出现超过 1 个孤立点(超过一个就会存在 2 种删除方式,答案就是 4 的倍数了),并且全程不应该同时出现大于等于 4 个孤立点(在模 4 意义下正确性显然)。那么我们可以发现合法的孤立点序列只有以下三种:
发现三种分别相当于最后剩下一个有 1 个,2 个,3 个叶子结点的菊花,考虑剩下节点删除的合法性。设每两轮删去的节点分别为 $ (u,v) $,显然有删除时 $ u $ 是 $ v $ 的父亲,并且 $ u $ 在当前时刻只有一个子节点,那么删除的方式会变得相当固定:对于子树大小为奇数的节点,显然要其所有儿子的子树大小都为偶数,且能全部在自己子树内匹配,此时这个节点会被留下给其父亲匹配。对于子树大小为偶数的节点,显然要其儿子子树的大小只有一个为奇数,其他儿子子树大小均为偶数且都是合法的(偶数能在子树内解决匹配,奇数只会留下一个点),该节点会和子树大小为奇数的那个节点匹配。考虑将每对匹配缩成一个点,对于原树中扣掉菊花的部分会留下若干个树,每个树内的儿子节点删去的时间都是在其父亲之前(否则删除后留下的不是单个孤立点,正确性显然)。
对于缩点后的一个森林设点集为 $ T $,设总共有 $ k $ 个点,则有不同的删除顺序数量为:
那么显然这个是好计算的,因为只需要维护这个值模 2 的值,进行一个 dp 根的换即可。
代码:
#include<bits/stdc++.h>
#define time(null) chrono::steady_clock::now().time_since_epoch().count()
#define int long long
#define uint unsigned long long
#define debug() cout<<"come here\n"
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define pb push_back
#define Code return
#define by 0
#define MCYYDS ;
using namespace std;
int qpow(int a,int b,int p=INF){int ret=1;while(b){if(b&1)ret=(ret*a)%p;a=(a*a)%p;b>>=1;}return ret;}
inline int read(){int ret=0,f=1;char ch=getchar();while(ch<'0'||ch>'9')f=(ch=='-'?-1:f),ch=getchar();while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+(ch^48),ch=getchar();return ret*f;}
inline void write(int x){if(x<0){putchar('-');write(-x);return ;}if(x>9)write(x/10);putchar((char)(x%10+48));}
inline void writech(int x,char ch){write(x);putchar(ch);}
int sz[1000005],son[1000005],ans,cnt,tot,n;
vector<int> e[1000005];
int check(int siz,int ctt)
{
if(siz&1)return (ctt!=0);
return (ctt!=1);
}
int getctz(int siz)
{
return (siz%2==0&&siz!=0?__builtin_ctz(siz/2):0);
}
void dfs(int u,int fa)
{
sz[u]=1;
for(auto v:e[u])
{
if(v==fa)continue;
dfs(v,u);
sz[u]+=sz[v];
if(sz[v]&1)son[u]++;
}
}
void dfs2(int u,int fa)
{
int msz=son[u]+1;
if(cnt==0&&msz<=4&&(n-msz)/2-__builtin_popcount((n-msz)/2)==tot)ans++;
for(auto v:e[u])
{
if(v==fa)continue;
sz[u]-=sz[v];
son[u]-=(sz[v]&1);
cnt-=check(sz[v],son[v]);
tot-=getctz(sz[v]);
sz[v]+=sz[u];
son[v]+=(sz[u]&1);
cnt+=check(sz[u],son[u]);
tot+=getctz(sz[u]);
dfs2(v,u);
tot-=getctz(sz[u]);
cnt-=check(sz[u],son[u]);
son[v]-=(sz[u]&1);
sz[v]-=sz[u];
tot+=getctz(sz[v]);
cnt+=check(sz[v],son[v]);
son[u]+=(sz[v]&1);
sz[u]+=sz[v];
}
}
void solve()
{
n=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e[u].pb(v);
e[v].pb(u);
}
if(n==1)
{
puts("1");
return ;
}
if(n==2)
{
puts("2");
e[1].clear();
e[2].clear();
return ;
}
dfs(1,0);
ans=cnt=tot=0;
for(int i=2;i<=n;i++)
{
cnt+=check(sz[i],son[i]);
tot+=getctz(sz[i]);
}
dfs2(1,0);
writech((ans%2)*2,'\n');
for(int i=1;i<=n;i++)
{
e[i].clear();
sz[i]=son[i]=0;
}
}
signed main()
{
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
int T=read();
while(T--)
{
solve();
}
Code by MCYYDS
}

浙公网安备 33010602011771号