题解:【ARC112C】 DFS Game
题目里面的注意点还是很多的,如果读错了题整个思路可能会一点都不对。首先是移动和选取硬币的操作是分开的,所以你移动到了一个有硬币的节点,将是你的对手获得硬币。然后移动和拿取都是单次操作的,进行后就换人了。
如果要使总答案最优,那每个节点的决策都要最优。选择了一个节点就要决策完他的整个子树。因此考虑树形DP,我们用儿子来推父亲,当决策一个节点的时候,他的子节点信息我们都已经知道。
设 \(g_i\) 表示点 \(i\) 的子树大小,\(f_i\) 表示在当前节点先手能够得到的最多硬币,那么后手能够得到的最多硬币我们记为 \(h_i = g_i - f_i\)。
模拟几个小数据,假设一个人选择了当前的一个子节点 \(i\) 进入,能够发现当 \(g_i\) 为奇数时,回到 \(i\) 的父亲后变成他的对手进行子节点的选择;当 \(g_i\) 为偶数时,回到 \(i\) 的父亲后仍为他自己选择下一个进入的子节点。
所以一个节点如果有奇数个 \(g\) 为奇数的子节点,最后会交换决策权;偶数个 \(g\) 为奇数的子节点,决策权不变。
我们对子节点的状态分类讨论,均假设为先手拿取了该节点 \(i\) 的硬币,接下来该由后手决策进入哪个子节点 \(j \in son_i\):
- \(g_j\) 为偶数且 \(f_j < h_j\),那么对于后手来说这个节点肯定是有利的,因为他能拿得多,还能回来后继续操控局面,所以只要有这类节点,后手必定会选,此类节点的转移方程为 \(f_i = f_i + f_j\)。
- \(g_j\) 为偶数且 \(f_j \ge h_j\),那么对于后手来说这个节点不是亏的就是没有贡献,所以会尽量避免选这类节点,此类节点的转移为 \(f_i = f_i + h_j\)。
- \(g_j\) 为奇数,注意到选择了这类节点后会交换决策权,所以后手会尽量选择优秀的节点——能让他比对手拿得更多的节点,即 \(h_i - f_i\) 尽可能大的节点。
所以整个游戏的流程至此我们能够确定下来了:先拿第一类节点,然后拿第三类节点。判断一下第三类节点总数的奇偶性,看选完第三类节点后会不会交换决策权,从而看第二类节点的贡献到底是给先手还是后手。
整个遍历流程是 \(\mathcal O(n)\) 的复杂度,由于需要我们决策具体哪个第三类节点更优,这里选择使用优先队列,所以总复杂度为 \(\mathcal O(n \log 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))
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*10)+(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*10)+(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]);
if(ch!='~') putchar(ch);
return;
}
}
using namespace FastIO;
namespace MTool
{
#define TA template<typename T,typename... Args>
#define TT template<typename T>
static const int Mod=998244353;
TT inline void Swp(T &a,T &b) {T t=a;a=b;b=t;}
TT inline void cmax(T &a,T b) {a=max(a,b);}
TT inline void cmin(T &a,T b) {a=min(a,b);}
TA inline void cmax(T &a,T b,Args... args) {a=max({a,b,args...});}
TA inline void cmin(T &a,T b,Args... args) {a=min({a,b,args...});}
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;}
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);}
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=100010;
static const int inf=2147483647;
static const int INF=4557430888798830399;
int n,x;
vector<int> G[MAX];
int siz[MAX],f[MAX];
priority_queue<pii> q;
inline void mian()
{
read(n);
for(int i=2;i<=n;++i) read(x),G[x].eb(i);
auto dfs=[&](auto dfs,int now)->void
{
siz[now]=1; int tot=0,cnt=0;
for(auto to:G[now]) dfs(dfs,to),siz[now]+=siz[to],tot^=siz[to]&1;
f[now]=1;
for(auto to:G[now])
{
if(siz[to]&1) q.emplace(mp(siz[to]-2*f[to],f[to]));
else
{
if(f[to]<siz[to]-f[to]||(f[to]>siz[to]-f[to]&&(!tot))) f[now]+=f[to];
else f[now]+=siz[to]-f[to];
}
}
while(!q.empty()) {++cnt,f[now]+=q.top().se; if(!(cnt&1)) f[now]+=q.top().fi; q.pop();}
};
dfs(dfs,1);
write(f[1],'\n');
}
}
bool Med;
signed main()
{
// file();
fprintf(stderr,"%.3lf MB\n",abs(&Med-&Mbe)/1048576.0);
int Tbe=clock();
LgxTpre::mian();
int Ted=clock();
cerr<<1e3*(Ted-Tbe)/CLOCKS_PER_SEC<<" ms\n";
return (0-0);
}