Gym.102059: 2018-2019 XIX Open Cup, Grand Prix of Korea(寒假gym自训第一场)
整体来说,这一场的质量比较高,但是题意也有些难懂。
E.Electronic Circuit
题意: 给你N个点,M根线,问它是否是一个合法的电路。
思路: 一个合法的电路,经过一些串联并联关系,最后有一个点是正极,一个是负极,我们就来模拟这个串联的过程即可,并联的关系会因为我们使用了set而抵消,所以可以不去管它。 模拟串联: 我们选择一个度数为2的点,删去它与两边的点u,v的连线,然后连接u-v,知道不存在度数为2的点。 最后当有两个点度数为1,而且不存在度数为大于大于2的点,则合法。
(选择set其实比较巧妙,因为不用考虑并联。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100010; set<int>S[maxn]; queue<int>q; int main() { int N,M,u,x,y; scanf("%d%d",&N,&M); rep(i,1,M){ scanf("%d%d",&x,&y); S[x].insert(y); S[y].insert(x); } rep(i,1,N) if(S[i].size()==2) q.push(i); while(!q.empty()){ u=q.front(); q.pop(); if(S[u].size()!=2) continue; x=*S[u].begin(); S[u].erase(S[u].begin()); y=*S[u].begin(); S[u].erase(S[u].begin()); S[x].erase(u); S[y].erase(u); S[x].insert(y); S[y].insert(x); if(S[x].size()==2) q.push(x); if(S[y].size()==2) q.push(y); } int cnt=0,ok=1; rep(i,1,N) { if(S[i].size()==1) cnt++; if(S[i].size()>1) ok=0; } if(ok&&cnt==2) puts("Yes"); else puts("No"); return 0; }
F .Fake Plastic Trees
题意:让你建造一棵树,使得它有N个叶子节点,而且需要满足对于每个节点,它的左子树大小等于右子树大小,或者左子树大小比右边大1。
每次你构造一棵树,你可以选择之前构造过的树作为它的儿子。 现在让你构造不超过125棵树,满足上诉条件。
输出的方式是,输出V,表示构造的树的多少。 接下来V上,每行表示左子树的标号和右子树的编号。 最后输出根节点的标号。
(读了好久才懂了题意,主要是输出这里,我们构造的东西每次都是重复利用了之前的子树的。
思路:每次除2构造即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100010; int x[maxn],y[maxn],tot; map<ll,int>mp; void get(ll num) { if(mp.find(num)!=mp.end()) return ; if(num==1LL) { x[++tot]=-1; y[tot]=-1; mp[num]=tot; return ; } ll mid=num/2; get(num-mid); get(mid); tot++; x[tot]=mp[num-mid]; y[tot]=mp[mid]; mp[num]=tot; } int main() { int T; ll N; scanf("%d",&T); while(T--){ scanf("%lld",&N); tot=-1; mp.clear(); get(N); printf("%d\n",tot+1); rep(i,0,tot) printf("%d %d\n",x[i],y[i]); printf("%d\n",tot); } return 0; }
H .Fractions
题意:求多少对(x,y),满足A<=x<=B ; C<=y<=D,而且(x+y)/gcd(x,y)<1000;
思路:水题,我们枚举化简后的x'=x/gcd; y'=y/gcd;然后看有多少gcd可以在给定区间即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; int main() { ll A,B,C,D,ans=0; scanf("%lld%lld%lld%lld",&A,&B,&C,&D); rep(i,1,999) rep(j,1,999-i){ if((__gcd(i,j))==1){ //A<=ix<=B c<=jx<=D ll tmp=min(D/j,B/i)-max((A-1)/i,(C-1)/j); if(tmp>0) ans+=tmp; } } printf("%lld\n",ans); return 0; }
I .Game on Plane
题意:给定N个点,围成一个圈,每次玩家选择两个点连线,不得与之前连的线相交。 如果玩家连线形成了一个多边形或者没有选的点,输。
思路:sg函数,每次连线会把大圈分为两个小圈,跑sg即可。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=5010; int sg[maxn],vis[maxn]; void init() { sg[1]=0; sg[2]=1; rep(i,3,5000){ rep(j,0,i-2){ vis[sg[j]^sg[i-2-j]]=i; } rep(j,0,5000) { if(vis[j]!=i) { sg[i]=j; break;} } } } int main() { init(); int T,N; scanf("%d",&T); while(T--){ scanf("%d",&N); if(!sg[N]) puts("Second"); else puts("First"); } return 0; }
L .Timsort
题意:给定长度为N的数组a[],Q次询问,每次给出长度L,让你按照规定跑。 每次从当前位置出发,一直跑不降序列,或者下降序列。跑完后,如果大于等于L,从下一个位置继续开始,否则要凑齐长度L,同时累计凑数的个数。
思路:预处理,每次按照规定跑即可,因为我们可以记忆化,所以我们可以假设每次的问题是不一样的,最坏的情况下是N/1+N/2+N/3...+N/N~=NlogN,所以就ok了,想不到这一点,这个水题就跑掉了。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define rep2(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int maxn=100010; int a[maxn],R[maxn][2],ans[maxn][2]; int main() { int N,Q,x; scanf("%d",&N); rep(i,1,N) scanf("%d",&a[i]); rep2(i,N,1) { R[i][0]=R[i][1]=1; if(i+1<=N&&a[i]<=a[i+1]) R[i][0]=R[i+1][0]+1; if(i+1<=N&&a[i]>a[i+1]) R[i][1]=R[i+1][1]+1; } scanf("%d",&Q); while(Q--){ scanf("%d",&x); if(ans[x][0]) printf("%d %d\n",ans[x][0],ans[x][1]); else { int A=0,B=0; rep(i,1,N){ A++; if(i==N) break; int t=R[i][a[i]>a[i+1]]; if(t<x){ if(i+x-1<=N) B+=x-t; else B+=N-(i+t-1); i=i+x-1; } else i=i+t-1; } ans[x][0]=A; ans[x][1]=B; printf("%d %d\n",A,B); } } return 0; }