Codeforces Round #541 (Div. 2) 題解
Codeforces Round #541 (Div. 2)
題目鏈接:https://codeforces.com/contest/1131
A. Sea Battle
題意:
給出兩個矩形的寬和高,滿足第一個矩形的左上頂點為(0,0),右下頂點為(w1,-h1);第二個矩形的坐下頂點為(0,0),右上頂點為(w2,h2)。然後求圍繞這兩個矩形一圈的格子個數為多少。
題解:
利用二維前綴和的思路推導一下公式就行了,公式也有很多吧==我當時就是大概模擬了一下。。
代碼如下:
#include <bits/stdc++.h> using namespace std; typedefView Codelong long ll; ll w1,h1,w2,h2; int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>w1>>h1>>w2>>h2; ll ans = 0; ans+=(h1+h2+2)*(max(w1,w2)+2); ll now=min(w1,w2); ll tmp=max(w1,w2)-now; if(w1<w2) ans-=h1*tmp; else ans-=h2*tmp; ans-=(w1*h1+w2*h2); cout<<ans; return 0; }
B. Draw!
題意:
給出n次兩個人的比分,每次給出xi,yi代表兩個人的比分,然後問這兩個人最多平局的多少次。
題解:
假設這兩個人在比分為p的時候平局,對於xi,yi,xi+1,yi+1來說,肯定滿足max(xi,yi)<=p<=min(xi+1,yi+1)的,然後就借用這個式子來算。註意一下xi==yi這種特殊情況,可能會被計算多次,處理一下就好了。
原諒我代碼對不上上面的題解。。懶得改了,我感覺我的做題思路還沒有抽象化,上面一題也是,基本都是采用模擬的方法來做的,沒有很好地進行數學建模。
代碼如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 10005; int n; int a[N],x[N],y[N]; int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n; ll ans = 1; for(int i=1;i<=n;i++) cin>>x[i]>>y[i]; ans+=min(x[1],y[1]); for(int i=2;i<=n;i++){ if(x[i]>y[i]){ int dx=x[i]-x[i-1],dy=y[i]-y[i-1]; if(y[i]<x[i-1]) continue ; if(y[i-1]<x[i-1]) ans+=(y[i]-x[i-1]+1); else if(y[i-1]>x[i-1]) ans+=dy+1; else ans+=min(dx,dy); }else{ int dx=x[i]-x[i-1],dy=y[i]-y[i-1]; if(x[i]<y[i-1]) continue ; if(x[i-1]<y[i-1]) ans+=(x[i]-y[i-1]+1); else if(x[i-1]>y[i-1]) ans+=dx+1; else ans+=min(dx,dy); } } cout<<ans; }View Code
C. Birthday
題意:
n個人,每個人都有對應的身高ai,然後這n個人站成一個圈,問怎樣的站法,能夠使相鄰兩個人的身高差最小,最後任意輸出一種方法即可。
題解:
考慮貪心的做法(為啥這題的數據為100啊...),先對所有的數排個序,然後從前往後依次放奇數位置的人,從後往前依次放偶數位置的人。
題解的證明用到了哈密頓回路,我們這種做法得到的答案為max(ai+2-ai),然後只需要證明不可能存在max(ai+1-ai)這樣的答案就行了,證明這個在直觀上面還是比較好理解的,很容易得證。
代碼如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 105; int a[N],ans[N]; int n; int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+n+1); int fir=1,last=n; for(int i=1;i<=n;i++){ if(i&1){ ans[fir++]=a[i]; }else ans[last--]=a[i]; } for(int i=1;i<=n;i++) cout<<ans[i]<<" "; return 0; }View Code
D. Gourmet choice
題意:
給出一個n*m的符號矩陣,分別對應第n個數和第m個數的大小關系,然後讓你根據這個矩陣構造這n+m個數,如果得不到就輸出"No"。
題解:
其實根據大小關系很容易想到拓撲排序,小的向大的連邊,然後如果無環就存在一種情況。
但是這裏有等於符號,在本題中,等於的那些數本質上是相等的,也就是說擁有相同的拓撲序,那我們用並查集縮點處理即可。
然後跑拓撲排序,途中標記答案,最後根據輸出就行了。
註意一下這種情況:就是如果一些點都屬於一個集合中,但是有些點的關系存在不等關系,這就矛盾了,要判斷一下。
代碼如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1005; int n,m,cnt,tot; char mp[N][N]; int f[N<<1],num[N<<1],head[N<<1],in[N<<1],ans[N<<1]; struct Edge{ int u,v,next; }e[N*N<<1]; int find(int x){ return f[x]==x?f[x]:f[x]=find(f[x]); } void Union(int x,int y){ int fx=find(x),fy=find(y); if(fx==fy) return ; f[fx]=fy; } void adde(int u,int v){ e[tot].v=v;e[tot].next=head[u];head[u]=tot++; } int topsort(){ queue <int> q; int tot=0; for(int i=1;i<=cnt;i++) if(!in[i]) q.push(i),tot++,ans[i]=1; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(--in[v]==0){ ans[v]=ans[u]+1; q.push(v); tot++; } } } return tot==cnt; } int main(){ scanf("%d%d",&n,&m); int flag=0; for(int i=1;i<=n+m;i++) f[i]=i; for(int i=1;i<=n;i++){ scanf("%s",mp[i]+1); for(int j=1;j<=m;j++){ if(mp[i][j]==‘=‘) Union(i,j+n); else flag=1; } } for(int i=1;i<=n+m;i++){ int f=find(i); if(!num[f]) num[f]=++cnt; } if(cnt==1 && flag){ cout<<"No"; return 0; } memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ int u=num[find(i)],v=num[find(j+n)]; if(u==v&&mp[i][j]!=‘=‘){ cout<<"No"; return 0; } if(mp[i][j]==‘<‘) adde(u,v),in[v]++; else if(mp[i][j]==‘>‘) adde(v,u),in[u]++; } } int f=topsort(); if(!f) cout<<"No"; else{ puts("Yes"); for(int i=1;i<=n+m;i++){ int fx=find(i); cout<<ans[num[fx]]<<" "; if(i==n) cout<<‘\n‘; } } return 0; }View Code
F. Asya And Kittens
題意:
有n個數亂序排列,每個數都互不相等,並且它們中間有n-1個隔板將它們分離。現在每次會抽出兩個數之中的隔板,進行n-1次操作,最終n個數之中沒有隔板,最後輸出這n個數排列的一種情況。註意題目中給出的兩個數,不一定是相鄰的,但它們一定在相鄰的兩個塊中。
題解:
對於兩個相鄰的塊,我們直接合並就行了,往左或者右合並都行,因為反正它們都會成為一個塊。這裏用並查集的話就要啟發式合並,減少空間的消耗,具體點就是將size小的集合往size大的集合合並,這樣可以節約總空間,並且可以證明空間復雜度不超過O(nlogn);如果不這樣合並,空間復雜度上界可能為O(n^2)。
當然這裏有個黑科技rope,用這個來儲存十分節約空間,對於這個題怎麽合並都行,並且單次插入、刪除等操作都是O(logn)的復雜度,常數稍微大了點,但是已經十分優秀了。這個底層是用平衡樹來搞的,似乎可以來代替塊狀鏈表= =反正就是個類似於鏈表的東西。
代碼如下:
#include <bits/stdc++.h> #include <ext/rope> using namespace std; using namespace __gnu_cxx; typedef long long ll; const int N = 150005; int n; int f[N]; rope <int> a[N]; int find(int x){ return f[x]==x ? f[x] : f[x]=find(f[x]); } int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n; for(int i=1;i<=n;i++) f[i]=i,a[i]=i; for(int i=1;i<n;i++){ int x,y; cin>>x>>y; int fx=find(x),fy=find(y); if(fx==fy) continue ; f[fx]=fy; a[fy]+=a[fx]; } for(int i=1;i<=n;i++){ int f=find(i); if(f==i){ for(int j=0;j<a[f].size();j++){ cout<<a[f][j]<<" "; } break ; } } return 0; }View Code
Codeforces Round #541 (Div. 2) 題解