题意: 一棵以点 1 为根的树,每个点上有 ai 个苹果,两个人轮流操作。 每次操作:选择一个非空的点,如果这个点是叶子结点,则可以吃掉这个点上任意个苹果;如果这个点不是叶子结点,则可以选择这个点上任意个苹果移动到它的一个儿子结点上。 但后手在游戏开始前可以交换任意两个点的苹果,问后手有多少种交换方案可以获胜。 (题目保证所有叶子结点到根结点路径长度的奇偶性相同)
tags: 就是阶梯博弈,所有点按到根结点路径长度奇偶性分两类,第一类与叶子结点奇偶性相同,第二类与叶子结点不同。 只要 dfs 求出第一类点的异或和,然后枚举即可。
// 812E #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define MP make_pair #define PB push_back #define fi first #define se second typedef long long ll; const int N = 100005; ll ans; int n, a[N], pi; vector<int > G[N]; int s[2], flag, cnt[2]; map<int , int > mp[2]; void upd(int u, int dep) { if((dep%2) != (flag%2)) { s[flag^1]^=a[u]; mp[flag^1][a[u]]++; ++cnt[flag^1]; } else { s[flag]^=a[u]; mp[flag][a[u]]++; ++cnt[flag]; } } void dfs(int u, int fa, int dep) { if(G[u].size()==1 && u!=1) { if((dep&1) && flag==0) flag^=1; } for(auto to : G[u]) if(to!=fa) dfs(to, u, dep+1); upd(u, dep); } int main() { scanf("%d", &n); rep(i,1,n) scanf("%d", &a[i]); rep(i,2,n) { scanf("%d", &pi); G[pi].PB(i); G[i].PB(pi); } dfs(1, 0, 0); if(s[flag]==0) { if(cnt[flag]>1) ans += 1LL*cnt[flag]*(cnt[flag]-1)/2; if(cnt[flag^1]>1) ans += 1LL*cnt[flag^1]*(cnt[flag^1]-1)/2; for(auto it=mp[flag].begin(); it!=mp[flag].end(); ++it) { int tmp1=it->fi, tmp2=it->se, tmp3=mp[flag^1][tmp1]; ans += 1LL*tmp2*tmp3; } } else { for(auto it=mp[flag].begin(); it!=mp[flag].end(); ++it) { int tmp1=it->fi, tmp2=it->se, tmp3=mp[flag^1][s[flag]^tmp1]; ans += 1LL*tmp2*tmp3; } } printf("%lld ", ans); return 0; }