Codeforces Round #417 (Div. 2) 题解
2017-07-15 20:56
162 查看
比赛地址:http://codeforces.com/contest/812
A. Sagheer and Crossroads
题目的意思是给出4个路口的车辆和人的红路灯状态,判断是否可能车撞人。
关键是题意,然后模拟,注意细节。
B. Sagheer, the Hausmeister
题意:n<15,m<=100的矩阵 1为亮,0为灭 每层第一个和最后一个位置为楼梯,每层必须灭完才能到上一层,问全部灯灭完 最小时间?
解法:到上一层只能从左端或者右端走 设dp[i][0/1] : 灭了前i层的灯 &&当前在第i层左/右端时的最小时间。
dp[i][0]=min(dp[i+1][0]+1+(r[i]−1)∗2,dp[i+1][1]+1+m−1)
第i层没灯时dp[i][0]=dp[i+1][0]坑点:只有一层,第一层为空...
C. Sagheer and Nubian Market
排序+二分水题。
D. Sagheer and Kindergarten
题意: 一共n个人,m个玩具,然后读入是x和y,表示x要玩y。然后在玩y的人玩的时间不知道(当他得到了所有想要的玩具之后就开始玩,玩完之后将玩具还回),如果x要玩y……..这样形成了一个环,这个环上所有人都会哭。比如1要玩2在玩的玩具,2要玩3的,3要玩1的,就形成了个环,就全都哭了。然后前面k个操作,保证没人哭,后面q个操作,问如果有这个操作,有多少个人哭了
解法:每个人最多只会等1个人玩某个玩具,从某个人出发连一条指向等的人的有向边。最终形成了一个有许多有根树的森林。每个结点最多1个出度,用欧拉路的方式给每个结点的出入标id,就方便判断某两点是否之前在一棵树上,以及在树的路径中经过的结点个数。这样每次询问,如果最后一个玩y的人在以x为根节点的子树上,连接这两个人就形成了一个环,输出这棵子树上结点个数即可。
E. Sagheer and Apple Tree
题意:有一颗很奇怪的苹果树,这个苹果树所有叶子节点的深度要不全是奇数,要不全是偶数,并且包括根在内的所有节点上都有若干个苹果,现有两个极其聪明的人又来无聊的游戏了,每个人可以吃掉某个叶子节点上的部分苹果(不能不吃),或者将某个非叶子结点上的部分苹果移向它的孩子(当然也不能不移),吃掉树上最后一个苹果的人获胜,问先手是否必胜?不,不是问这个,因为后手可以作弊,他可以交换任意两个节点的苹果数量,问有多少种不同的作弊(交换)方式可以使得后手必胜(不能不交换)?
解法:书上的Nim游戏。
中文版是这位同学写的,尊重版权从我做起。点这里
不如考虑什么时候先手必胜,在尼姆博弈中,将所有的苹果个数依次异或,如果最后答案为0就先手必败,否则必胜,那么树上的呢?其实一样,不妨把节点按照深度分成两种:①想要移动到任意叶子结点都需要偶数步数(深度和最深的叶子结点深度奇偶性相同);②想要移动到任意叶子结点都需要奇数步数
对于①,玩家只要移动x个苹果到它孩子节点上,那么这x个苹果就一定会变成状态②之下
对于②,玩家只要移动x个苹果到它孩子节点上,那么这x个苹果就一定会变成状态①之下
而孩子结点属于状态①,所以当前玩家只要移动状态②下的x个苹果,那么另一个玩家只要照搬你的移动,将这x个苹果再次移动到当前的孩子,就又变成状态②了,若当前已经到叶子节点无法移动了,另一个玩家就可以直接将这x个苹果吃掉(你就GG,这几个苹果就和没有一样!)
这样就很明显了,先手对于状态②下的苹果,无论怎么移动,最后一定都会被聪明的后手玩家用上述方法吃掉,所以状态②的苹果毫无意义,那么状态①的呢,只要移动一部就会变得状态②从而毫无意义(和没有一样),那这不就转化成了裸的尼姆博弈了么?
结论:只要将所有状态①下的苹果数量异或,最后结果ans若为0则先手必败,否则必胜!
那么怎么解决这道题,其实已经好办了,先判断一波先手必胜还必败。
如果已经必败了,那么后手作弊交换节点时就要尽量避免改变战局,要知道a^b==b^a,异或是满足交换律的,所以B可以将所以状态①中任意两个节点互换,或者将状态②中任意两个节点互换,或者将分别属于状态①和状态②中但数量相同的两堆互换,三种情况个数加在一起即是答案。
如果先手必胜,那么后手的交换一定要改变战局,这个时候必须将状态①中的某个节点和状态②中的某个节点互换,那么怎么换呢?亦或性质a^b^b==a,假设当前异或结果是ans,那么只要遍历一遍状态①中的所有节点,对于当前节点的苹果个数temp,在状态②中找到苹果个数为ans^temp的节点,交换即可!最后统计一遍个数便是答案
这题关键是思维,代码就简单了。
总结:这次比赛的思维能力要求的很高,但是代码写起来很短。所以思维是首先要考虑的。
A. Sagheer and Crossroads
题目的意思是给出4个路口的车辆和人的红路灯状态,判断是否可能车撞人。
关键是题意,然后模拟,注意细节。
#include <bits/stdc++.h> using namespace std; int l[5], s[5], r[5], p[5]; int main() { for(int i=1; i<=4; i++){ scanf("%d %d %d %d", &l[i],&s[i],&r[i],&p[i]); } if(p[1]==1){ if(l[1] || s[1] || r[1] || l[2] || s[3] || r[4]){ puts("YES"); return 0; } } if(p[2]==1){ if(l[2] || s[2] || r[2] || r[1] || l[3] || s[4]){ puts("YES"); return 0; } } if(p[3]==1){ if(l[3] || s[3] || r[3] || l[4] || s[1] || r[2]){ puts("YES"); return 0; } } if(p[4]==1){ if(l[4] || s[4] || r[4] || l[1] || r[3] || s[2]){ puts("YES"); return 0; } } puts("NO"); return 0; }
B. Sagheer, the Hausmeister
题意:n<15,m<=100的矩阵 1为亮,0为灭 每层第一个和最后一个位置为楼梯,每层必须灭完才能到上一层,问全部灯灭完 最小时间?
解法:到上一层只能从左端或者右端走 设dp[i][0/1] : 灭了前i层的灯 &&当前在第i层左/右端时的最小时间。
dp[i][0]=min(dp[i+1][0]+1+(r[i]−1)∗2,dp[i+1][1]+1+m−1)
第i层没灯时dp[i][0]=dp[i+1][0]坑点:只有一层,第一层为空...
#include <bits/stdc++.h> using namespace std; int n, m, ans=0, l[300], r[300], dp[300][2]; char s[300]; int main() { scanf("%d%d",&n,&m); m+=2; int maxx=n+1; for(int i=1; i<=n; i++){ l[i]=1e5,r[i]=0; scanf("%s",s+1); for(int j=1; j<=m; j++){ if(s[j]=='1'){ l[i]=min(l[i],j); r[i]=max(r[i],j); maxx=min(i,maxx); } } if(l[i]==1e5){ l[i]=1,r[i]=m; } } for(int i=n; i>=maxx; i--){ if(r[i]==m){ if(i==n) dp[i][0]=0,dp[i][1]=m-1; else{ dp[i][0]=dp[i+1][0]+1; dp[i][1]=dp[i+1][1]+1; } continue; } if(i==maxx){ ans=min(dp[i+1][0]+1+r[i]-1,dp[i+1][1]+1+(m-l[i])); break; } if(i==n){ dp[i][0]=2*(r[i]-1); dp[i][1]=m-1; continue; } dp[i][0]=min(dp[i+1][0]+1+2*(r[i]-1),dp[i+1][1]+m-1+1); dp[i][1]=min(dp[i+1][1]+(m-l[i])*2+1,dp[i+1][0]+m-1+1); } if(maxx==n) ans=r -1; cout<<ans<<endl; }
C. Sagheer and Nubian Market
排序+二分水题。
#include <bits/stdc++.h> using namespace std; typedef long long LL; int n; LL S,a[100010], b[100010]; LL check(int mid){ for(int i=1; i<=n; i++) b[i]=a[i]+1LL*i*mid; sort(b+1,b+n+1); LL res=0; for(int i=1; i<=mid; i++){ res+=b[i]; } return res; } int main(){ cin>>n>>S; for(int i=1; i<=n; i++){ scanf("%lld",&a[i]); } int ans=1; int l=0,r=n; while(l<=r){ int mid=(l+r)/2; if(check(mid)<=S) ans=mid,l=mid+1; else r=mid-1; } printf("%d %lld\n", ans,check(ans)); }
D. Sagheer and Kindergarten
题意: 一共n个人,m个玩具,然后读入是x和y,表示x要玩y。然后在玩y的人玩的时间不知道(当他得到了所有想要的玩具之后就开始玩,玩完之后将玩具还回),如果x要玩y……..这样形成了一个环,这个环上所有人都会哭。比如1要玩2在玩的玩具,2要玩3的,3要玩1的,就形成了个环,就全都哭了。然后前面k个操作,保证没人哭,后面q个操作,问如果有这个操作,有多少个人哭了
解法:每个人最多只会等1个人玩某个玩具,从某个人出发连一条指向等的人的有向边。最终形成了一个有许多有根树的森林。每个结点最多1个出度,用欧拉路的方式给每个结点的出入标id,就方便判断某两点是否之前在一棵树上,以及在树的路径中经过的结点个数。这样每次询问,如果最后一个玩y的人在以x为根节点的子树上,连接这两个人就形成了一个环,输出这棵子树上结点个数即可。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5+5; int n, m, k, q, dfsclk, fa[maxn], pre[maxn], in[maxn], out[maxn]; vector <int> G[maxn]; void dfs(int u){ in[u] = ++dfsclk; for(int i=0; i<G[u].size(); i++){ dfs(G[u][i]); } out[u] = dfsclk; } int main() { while(~scanf("%d %d %d %d" , &n,&m,&k,&q)) { for(int i=0; i<maxn; i++) G[i].clear(); memset(fa, 0, sizeof(fa)); memset(pre, 0, sizeof(pre)); memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); dfsclk = 0; for(int i=1; i<=k; i++){ int x,y; scanf("%d%d",&x,&y); if(pre[y]) fa[x] = pre[y];//如果有人在x前一个玩y,x得等到前一个玩y的结束 pre[y] = x; } for(int i=1; i<=n; i++) G[fa[i]].push_back(i); for(int i=1; i<=n; i++){ if(!in[i]) dfs(i); } while(q--){ int x, y; scanf("%d %d", &x, &y); y = pre[y]; //最后一个玩y的人 if(in[x]<=in[y]&&out[x]>=in[y]){ printf("%d\n", out[x]-in[x]+1); } else{ printf("0\n"); } } } return 0; }
E. Sagheer and Apple Tree
题意:有一颗很奇怪的苹果树,这个苹果树所有叶子节点的深度要不全是奇数,要不全是偶数,并且包括根在内的所有节点上都有若干个苹果,现有两个极其聪明的人又来无聊的游戏了,每个人可以吃掉某个叶子节点上的部分苹果(不能不吃),或者将某个非叶子结点上的部分苹果移向它的孩子(当然也不能不移),吃掉树上最后一个苹果的人获胜,问先手是否必胜?不,不是问这个,因为后手可以作弊,他可以交换任意两个节点的苹果数量,问有多少种不同的作弊(交换)方式可以使得后手必胜(不能不交换)?
解法:书上的Nim游戏。
中文版是这位同学写的,尊重版权从我做起。点这里
不如考虑什么时候先手必胜,在尼姆博弈中,将所有的苹果个数依次异或,如果最后答案为0就先手必败,否则必胜,那么树上的呢?其实一样,不妨把节点按照深度分成两种:①想要移动到任意叶子结点都需要偶数步数(深度和最深的叶子结点深度奇偶性相同);②想要移动到任意叶子结点都需要奇数步数
对于①,玩家只要移动x个苹果到它孩子节点上,那么这x个苹果就一定会变成状态②之下
对于②,玩家只要移动x个苹果到它孩子节点上,那么这x个苹果就一定会变成状态①之下
而孩子结点属于状态①,所以当前玩家只要移动状态②下的x个苹果,那么另一个玩家只要照搬你的移动,将这x个苹果再次移动到当前的孩子,就又变成状态②了,若当前已经到叶子节点无法移动了,另一个玩家就可以直接将这x个苹果吃掉(你就GG,这几个苹果就和没有一样!)
这样就很明显了,先手对于状态②下的苹果,无论怎么移动,最后一定都会被聪明的后手玩家用上述方法吃掉,所以状态②的苹果毫无意义,那么状态①的呢,只要移动一部就会变得状态②从而毫无意义(和没有一样),那这不就转化成了裸的尼姆博弈了么?
结论:只要将所有状态①下的苹果数量异或,最后结果ans若为0则先手必败,否则必胜!
那么怎么解决这道题,其实已经好办了,先判断一波先手必胜还必败。
如果已经必败了,那么后手作弊交换节点时就要尽量避免改变战局,要知道a^b==b^a,异或是满足交换律的,所以B可以将所以状态①中任意两个节点互换,或者将状态②中任意两个节点互换,或者将分别属于状态①和状态②中但数量相同的两堆互换,三种情况个数加在一起即是答案。
如果先手必胜,那么后手的交换一定要改变战局,这个时候必须将状态①中的某个节点和状态②中的某个节点互换,那么怎么换呢?亦或性质a^b^b==a,假设当前异或结果是ans,那么只要遍历一遍状态①中的所有节点,对于当前节点的苹果个数temp,在状态②中找到苹果个数为ans^temp的节点,交换即可!最后统计一遍个数便是答案
这题关键是思维,代码就简单了。
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; typedef long long LL; struct edge{ int to, next; }E[maxn*2]; int head[maxn], edgecnt; void init(){ edgecnt = 0; memset(head,-1, sizeof(head)); } void add(int u, int v){ E[edgecnt].to = v, E[edgecnt].next = head[u], head[u] = edgecnt++; } map <LL, LL> mp1,mp2; int dep[maxn]; LL a[maxn], mxdep; vector <LL> v; void dfs(int u, int fa, int d){ dep[u] = d; mxdep = max(mxdep, 1LL*d); for(int i=head[u]; ~i; i=E[i].next){ int v = E[i].to; if(v!=fa){ dfs(v,u,d+1); } } } int main() { int n; while(~scanf("%d", &n)){ v.clear(); mp1.clear(); mp2.clear(); for(int i=1; i<=n; i++) scanf("%lld", &a[i]), v.push_back(a[i]); sort(v.begin(), v.end()); v.erase(unique(v.begin(),v.end()),v.end()); init(); for(int i=2; i<=n; i++){ int x; scanf("%d", &x); add(x, i); add(i, x); } mxdep = -1; dfs(1,-1,1); LL ans = 0, cnt1 = 0, cnt2 = 0; for(int i=1; i<=n; i++){ if(mxdep%2==dep[i]%2){ mp1[a[i]]++; ans ^= a[i]; cnt1++; } else{ mp2[a[i]]++; cnt2++; } } if(ans == 0){ ans = cnt1*(cnt1-1)/2+cnt2*(cnt2-1)/2; for(int i=0; i<v.size(); i++){ ans += mp1[v[i]]*mp2[v[i]]; } printf("%lld\n", ans); } else{ LL temp = ans; ans = 0; for(int i=1; i<=n; i++){ if(mxdep%2==dep[i]%2){ ans += mp2[temp^a[i]]; } } printf("%lld\n", ans); } } return 0; }
总结:这次比赛的思维能力要求的很高,但是代码写起来很短。所以思维是首先要考虑的。
相关文章推荐
- Codeforces Round #248 (Div. 2) (ABCD题解)
- Codeforces Round #304 (Div. 2) 题解
- Codeforces Round #313 (Div. 2) (ABCDE题解)
- Codeforces Round #320 div2 题解
- codefroces round 320 div2 题解
- Codeforces Round #428 (Div. 2) 题解(ABC)
- codeforces round 321 div2 题解
- Codeforces Round #361 (Div. 2) 题解 粗鲁地二分 组合数学提炼模型
- Codeforces Round #466 (Div. 2) 题解940A 940B 940C 940D 940E 940F
- Codeforces Round #375 (Div. 2)题解
- Investigating Div-Sum Property UVA - 11361(数位DP) 题解
- Codeforces Round #429 (Div. 2) 题解
- Codeforces Round #375 (Div. 2) ABCDEF题解
- CF 251 div2 题解
- Codeforces Round #441 (Div. 2, by Moscow Team Olympiad) ABCD题解
- Codeforces Round #301 (Div. 2) 题解
- Codeforces Round #392 (Div. 2) 题解
- Codeforces Round #411 (Div. 2) 题解
- Codeforces #310 Div 1 简要题解
- Codeforces Round #311 (Div. 2)题解