题解:【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\)

  1. \(g_j\) 为偶数且 \(f_j < h_j\),那么对于后手来说这个节点肯定是有利的,因为他能拿得多,还能回来后继续操控局面,所以只要有这类节点,后手必定会选,此类节点的转移方程为 \(f_i = f_i + f_j\)
  2. \(g_j\) 为偶数且 \(f_j \ge h_j\),那么对于后手来说这个节点不是亏的就是没有贡献,所以会尽量避免选这类节点,此类节点的转移为 \(f_i = f_i + h_j\)
  3. \(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);
}
posted @ 2023-03-19 09:01  LgxTpre  阅读(59)  评论(1编辑  收藏  举报