Codeforces 812E(Nim变形)

题意:一棵树(1为根,所有叶子节点深度同奇偶),每个节点上有一些苹果。现有两种操作:1.吃掉叶子节点上的苹果;2.将非叶子节点上的苹果送给儿子节点。两人轮流操作,无法操作的人输,现在后手玩家可以任意交换两个节点的苹果数,问有多少种交换方法使得后手胜利(必须交换)。

题解:将所有节点分为两类,深度与叶子节点同奇偶(蓝)、深度与叶子节点奇偶不同(红)。有结论:当所有蓝色节点异或和为 0 时,后手必胜。因此需要寻找有多少种交换方法可以使得蓝色节点异或和为0。

证明:可以将所有的蓝色节点看作Nim游戏中的石堆,现在的操作可以放入(将红色节点的苹果送给蓝色儿子)也可以拿出(吃掉蓝色叶子结点的苹果 or 将蓝色节点的苹果送给红色儿子)。因此结论与Nim相同:蓝色节点异或和为 0 时,后手必胜。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 int n,s,p;
 5 int a[100005];
 6 int in[100005];
 7 int vis[100005];
 8 vector<int> v[2];
 9 map<int,int> num[2];
10 vector<int> g[100005];
11 int dfs(int u,int deep){
12     vis[u]=(deep%2);
13     v[deep%2].push_back(a[u]);
14     num[deep%2][a[u]]++;
15     for(int i=0;i<g[u].size();i++){
16         dfs(g[u][i],deep+1);
17     }
18 }
19 int main(){
20     s=0;
21     scanf("%d",&n);
22     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
23     for(int i=2;i<=n;i++){
24         scanf("%d",&p);
25         g[p].push_back(i);
26         in[p]++;
27     }
28     dfs(1,0);
29     int p=-1;
30     for(int i=1;i<=n;i++){
31         if(in[i]==0){
32             p=vis[i];
33             break;
34         }
35     }
36     for(int i=0;i<v[p].size();i++) s^=v[p][i];
37     LL ans=0;
38     if(s==0){
39         LL l1=v[0].size(),l2=v[1].size();
40         ans+=l1*(l1-1)/2+l2*(l2-1)/2;
41         for(int i=0;i<v[p].size();i++){
42             int tmp=v[p][i];
43             ans+=(LL)num[p][tmp]*num[1-p][tmp];
44             num[p][tmp]=num[1-p][tmp]=0;
45         }
46     }
47     else{
48         for(int i=0;i<v[p].size();i++){
49             int tmp=s^v[p][i];
50             ans+=(LL)num[p][v[p][i]]*num[1-p][tmp];
51             num[p][v[p][i]]=num[1-p][tmp]=0;
52         }
53     }
54     printf("%lld\n",ans);
55 
56     return 0;
57 }
Psong

 

posted on 2017-06-02 12:22  Psong  阅读(252)  评论(0编辑  收藏  举报

导航