整體來說,這一場的質量比較高,但是題意也有些難懂。
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; }