Codeforces Round #535 (Div. 3) [codeforces div3 難度測評]

hhhh感受我真的過久沒有接觸過OI了算法

大約是前天聽到JK他們約着一塊兒刷codeforces,假期裏以爲有些頹廢的我突然也心血來潮來看看題目數組

今天看codeforces才知道竟然有div3了,感受應該看名字比div2還要簡單吧,因而我就作了作....發現確實還蠻簡單的hhhhide

可是我又突發奇想,乾脆更新一篇博客吧,畢竟這也是我少有的能刷完一整套CF的題,那也能夠記錄一下啦...(雖然div3的題解彷佛拿來充當一個題解仍是有點水的hhhh)spa

A - Two distinct points3d

題目大意:n組數據,每次給你[l1,r1]和[l2,r2]讓你輸出兩個不一樣的元素a,b,使得a在[l1,r1]中,b在[l2,r2]中,題目保證必定有解code

這兩個數的限制實在是....沒什麼限制hhhh,第一眼看到竟然感受不知道怎麼下手。blog

而後就想了一下,那要不選個a就不在[l2,r2]裏面,而後b就能夠隨便選,發現這樣的話就要比一下什麼區間誰在前誰在後,或者誰包含誰什麼的,實在有點麻煩。排序

後面想了一下,唔那我a就選個端點吧,感受它容易不在[l2,r2]裏面一些,那再想一下,那我b也選端點吧ip

因而那就a=l1或r1,b=l2或r2,而後要求a!=b,這就實在太水了hhhh 果真是div3,不過既然這是一篇題解,我就水到底吧:字符串

因此咱們判斷一下l1是否是等於l2

  若是不相等,那就a=l1,b=l2;

  若是相等的話,我就比一下l1是否是等於r2

    若是不等就a=l1,b=r2,;

    若是相等,說明l1==l2==r2,那麼b就必定要等於l2,題目又保證有解,那就有r1!=l2,那麼a=r1,b=l2就行了。

 

 1 #include<cstdio>
 2 
 3 using namespace std;  4 
 5 int main(){  6     int Kase,l1,r1,l2,r2;  7     scanf("%d",&Kase);  8     while(Kase--){  9         scanf("%d%d%d%d",&l1,&r1,&l2,&r2); 10         if(l1!=l2) 11             printf("%d %d\n",l1,l2); 12         else if(l1!=r2) 13             printf("%d %d\n",l1,r2); 14         else
15             printf("%d %d\n",r1,l2); 16  } 17     return 0; 18 }
View Code

而後交上去,刷新,1A!哇好開心啊! [雖然不知道這種題過了有什麼好開心的Hhhhh,多是codeforce的題確實會給你一種實現超簡單,可是想法是本身首創的這種很棒的成就感吧]

不過也就對B題也充滿了信心。

B. Divisors of Two Integers

題目大意:這題大概就是先告訴你一種得到集合的方法:給你x和y,而後把他們的因子分別放到集合A,B中去,最後把A,B直接融合在一塊兒,變成一個大的可重複元素的集合C。題目給你這個集合C,讓你求出x和y的確切值。集合大小<200,元素大小<10000

剛開始感受這大約是一個數學題,可能須要分解因式什麼的,或者是什麼經過最大公約數來反推兩個元素的值的題。

而後突然發現,好像它是全部的因子都在裏面hhh,也就是包括了x和y本身,因此我只須要經過找最大值確定就能找到x和y中的一個,不妨令x是那個最大的。

那麼我如今的任務就是把x的因子從集合C中拿走,而後剩下的就是集合B了,那麼剩下的裏面最大的就是元素y了。

固然你沒有必要真的把x的全部因子從集合中拿走,你只須要判斷一下集合中的某個元素可否被x整除就好了,而後剩下的不能整除的最大的就是y。

上面是y不是x的因子的狀況,若是y是x的因子,那麼你就須要找到一個最大的出現了兩次的元素,由於元素大小<10000因此能夠用桶來實現,若是元素大小大到不能用桶的話,就能夠用排序來實現。

 1 #include<cstdio>
 2 
 3 const int maxn=210;  4 
 5 int n;  6 int a[maxn],cnt[10010];  7 int x,y,z;  8 
 9 int main(){ 10 #ifndef ONLINE_JUDGE 11     freopen("x.in","r",stdin); 12 #endif
13 
14     scanf("%d",&n); 15     for(int i=1;i<=n;i++){ 16         scanf("%d",&a[i]); 17         x=x>a[i]?x:a[i]; 18  } 19     for(int i=1;i<=n;i++){ 20         if(x%a[i]) 21             y=y>a[i]?y:a[i]; 22         else if(++cnt[a[i]]>=2) 23             z=z>a[i]?z:a[i]; 24  } 25     y=(y==0)?z:y; 26     printf("%d %d",x,y); 27 
28     return 0; 29 }
View Code

 

而後又是1A,哇太感動了,簡直神清氣爽,因而對C題也充滿了自信。

C. Nice Garland

題目大意:給你一個只包含RGB三種字符的字符串,但願你把它改形成它想要的樣子,它想要的樣子就是相同顏色的元素之間的距離爲3的倍數。字符串大小爲200000,須要輸出一個改造最少的次數以及改造後的字符串。

剛開始看到改造字符串這種題,就感受是個DP,而後研究了一下樣例,發現好像樣例輸出都是相似RGBRGBRG...或者BRGBRGBRG...唔,而後發現好像它這個要求相同顏色的元素之間的距離都爲3的倍數的要求確實十分苛刻了。

例如第一個元素你放上了R,那麼你考慮它後面的三個元素R _ _ _,若是第三個位置上不放R,那麼只能放G或者B,不妨假設放了G

R _ _ G 那麼你會發現第二個空位只能放B,由於若是放R或者G都會讓距離不知足3的倍數的條件,因而就變成了R B _ G 而後第三個空位上就什麼也放不了了。

因此若是第一個元素是R,那麼第四個元素就必須也是R,因此就只有6種全排列的擴展串或者擴展串的子串了,因而就只須要比較6次取最小的就行了。

 1 /*
 2  File : C.cpp  3  Author : Robert_Yuan  4  Date : 2019/1/29  5  Discription :  6 */
 7 #include<cstdio>
 8 #include<cstring>
 9 
10 using namespace std; 11 
12 const int maxn=200010; 13 
14 int n,ans,ansi; 15 char ch[maxn]; 16 char s[6][4]={"RGB","RBG","GRB","GBR","BRG","BGR"}; 17 
18 int main(){ 19 #ifndef ONLINE_JUDGE 20     freopen("x.in","r",stdin); 21 #endif
22 
23     scanf("%d%s",&n,ch); 24     ans=n; 25     for(int i=0;i<6;i++){ 26         int cnt=0; 27         for(int j=0;j<n;j+=3){ 28             cnt+=(ch[j]!=s[i][0])&(j<n); 29             cnt+=(ch[j+1]!=s[i][1])&(j+1<n); 30             cnt+=(ch[j+2]!=s[i][2])&(j+2<n); 31  } 32         if(cnt<ans) 33             ans=cnt,ansi=i; 34  } 35     printf("%d\n",ans); 36     for(int i=0;i<n;i+=3){ 37         if(i<n) printf("%c",s[ansi][0]); 38         if(i+1<n) printf("%c",s[ansi][1]); 39         if(i+2<n) printf("%c",s[ansi][2]); 40  } 41 
42     return 0; 43 }
View Code

 

這題我maxn打錯了RE了一次,因而開始慢慢謹慎起來Hhh,接着是D題

D. Diverse Garland

題目大意:仍是一個只包含RGB三種字符的字符串,但願你把它改形成相鄰的兩個元素不相等的樣子,問你最少須要改造多少次,而後輸出改造後的字符串。字符串長度<200000.

這個就很經典了,就是上面想到的DP了,f[i][j]表示第i個元素修改爲j的代價,其中j只能取0,1,2

而後轉移方程大概就是每次從前一個是那種顏色轉移過來,若是枚舉的顏色和當前的不同就要+1,同樣的話就不用加

f[i][0]=Min(f[i-1][1],f[i-1][2])+(ch[i]!='R'); [其餘兩個也相似]

而後用一個pre[i][j]記錄一下當前狀態從i-1的哪個狀態轉移過來,就能夠輸出改造後的字符串了。

 1 /*
 2   File :
 3   Author : Robert_Yuan
 4   Date : 2019/1/29
 5   Discription :
 6 */
 7 #include<cstdio>
 8 #include<cstring>
 9 
10 using namespace std;
11 
12 const int maxn=200010;
13 
14 int n;
15 int f[maxn][3],pre[maxn][3];
16 char ch[maxn];
17 char Turn[3]={'G','R','B'};
18 
19 int main(){
20 #ifndef ONLINE_JUDGE
21     freopen("x.in","r",stdin);
22 #endif
23 
24     scanf("%d%s",&n,ch);
25     memset(f,0x3f,sizeof(f));
26     for(int i=0;i<3;i++)
27         f[0][i]=(ch[0]!=Turn[i]);
28     for(int i=1;i<n;i++)
29         for(int j=0;j<3;j++)
30             for(int k=0;k<3;k++)
31                 if(j!=k){
32                     int val=f[i-1][k]+(ch[i]!=Turn[j]);
33                     if(f[i][j]>val)
34                         f[i][j]=val,pre[i][j]=k;
35                 }
36     int ans=0x3f3f3f3f,ansi;
37     for(int i=0;i<3;i++){
38         if(ans>f[n-1][i])
39             ans=f[n-1][i],ansi=i;
40     }
41     for(int i=n-1;i>=0;i--){
42         ch[i]=Turn[ansi];
43         ansi=pre[i][ansi];
44     }
45     printf("%d\n",ans);
46     printf("%s",ch);
47     return 0;
48 } 
View Code

這題由於實在經典,因此彷佛沒有以前獲得的樂趣那麼多了。不過能1A仍是比較開心的。

感受div3從這裏終於進入了div2的難度

難道?!div3的C題至關於div2的A題....唔那好吧,感受頗有道理

 

E1. Array and Segments (Easy version)

題目大意:給你一個長度爲n的數組,而後給你m個區間,而後你能夠選擇若干個區間,讓這個區間內的每一個數-1,最後但願整個數組的最大值-最小值最大。最後要輸出這個最大的最大值-最小值,還有輸出你選擇的區間。

E1是一個簡單版本,n,m<300

由於是最大值減去最小值最大,因此咱們須要知道最後減完以後的最大值和最小值分別在什麼位置,那既然n,m這麼小,個人想法就是枚舉最後的最小值在哪一個位置,而後去枚舉區間,看看哪一個區間包括了這個最小值,就選擇這個區間來減,由於我必定但願最後的最小值更小,因此每一個能讓它變小的機會都不能放過,並且即便我選擇區間可能讓最後的最大值也減少了,可是由於是同時減少1,因此這個差值仍是不變的,因此這樣去選擇區間,最大值-最小值的值確定是只增不減的。

而後我就這樣打了,而後區間減的話,懶得打線段樹來作了,複雜度是O(n*m*n),發現嗯300^3能夠過。

 1 /*
 2   File :
 3   Author : Robert_Yuan
 4   Date : 2019/1/29
 5   Discription :
 6 */
 7 #include<cstdio>
 8 
 9 const int maxn=310;
10 
11 struct Node{
12     int l,r;
13 }s[maxn];
14 
15 int ans,ansi;
16 int n,m;
17 int a[maxn],b[maxn];
18 int st[maxn],tp;
19 
20 int judge(int t){
21     for(int i=1;i<=n;i++)
22         b[i]=a[i];
23     for(int i=1;i<=m;i++){
24         if(s[i].l<=t && s[i].r>=t)
25             for(int j=s[i].l;j<=s[i].r;j++)
26                 b[j]--;
27     }
28     int MAX=b[1],MIN=b[1];
29     for(int i=2;i<=n;i++){
30         MAX=MAX>b[i]?MAX:b[i];
31         MIN=MIN<b[i]?MIN:b[i];
32     }
33     return MAX-MIN;
34 }
35 
36 int main(){
37 #ifndef ONLINE_JUDGE
38     freopen("x.in","r",stdin);
39 #endif
40     scanf("%d%d",&n,&m);
41     for(int i=1;i<=n;i++)
42         scanf("%d",&a[i]);
43     for(int i=1;i<=m;i++)
44         scanf("%d%d",&s[i].l,&s[i].r); 
45     
46     for(int i=1;i<=n;i++){
47         int t=judge(i);
48         if(t>ans)
49             ans=t,ansi=i;
50     }
51     printf("%d\n",ans);
52     for(int i=1;i<=m;i++)
53         if(s[i].l<=ansi && s[i].r>=ansi)
54             st[++tp]=i;
55     printf("%d\n",tp);
56     for(int i=1;i<=tp;i++)
57         printf("%d ",st[i]);
58     return 0; 
59 }
View Code

1A,並且意外發現速度還能夠。

而後看E2

E2的題目描述如出一轍,而後只是把n改爲了200000。

因而我想,那就是逼着我寫線段樹來實現區間加和區間減了?而後一看tag有data structure,嗯嗯,更加印證了。

不過在枚舉誰是最後的最小值的時候,我以前的暴力有複製一遍原數組的操做,剛開始我想這個線段樹要是也這樣,那我豈不是要寫一個可持久化線段樹?而後感受空間什麼的就要算,有點小麻煩。不過又仔細想了一下,發現我只要每次作一個區間加就能夠把以前的恢復到最初狀態了,也就省了一個可持久化線段樹。不過只要用到了線段樹,那麼時間複雜度至少仍是在O(nmlogn)這個數量級,感受仍是會超時。

因而觀察一下這個算法中最佔時間的,就是在選擇每個位置來假設它是最後的最小值這個操做上,因而我就想應該不是全部的位置均可能是最小值,由於n>>m,因此有的位置它們在選擇區間上是等價的 [就是說若是選它們做爲最小值,選擇到的區間會是如出一轍的],因此在這些能夠選到相同區間的位置中,只有這一段中的最小值可能成爲最後的最小值,同時由於最後要求的值是最大值-最小值,因此這樣一片具備相同選擇區間的位置中,我只須要留下最大值和最小值就能夠了,同時由於只有m個區間,也就是2*m個端點,那麼我在每兩個端點之間的點的區間選擇上必定是等價的,因此我最後能夠把n縮小到(3*2*m)[最大值、最小值、端點]這樣一個量級,也就是1800左右,那麼如今nmlogn就徹底能夠了,甚至於不用線段樹可能也能過。

因而我就寫了一個不用線段樹的試了一下,發現誒?!真的過了Hhhhhh,那我就懶得寫線段樹的區間加減和取最大最小值了。

因此這題大概就是隻用了一個離散化的思想就過了...感受作出來也還蠻有成就感的。

 1 /*
 2   File :
 3   Author : Robert_Yuan
 4   Date : 2019/1/29
 5   Discription :
 6 */
 7 #include<cstdio>
 8 #include<algorithm>
 9 
10 using namespace std;
11 
12 const int maxn=100010;
13 const int maxm=310;
14 
15 struct Node{
16     int l,r;
17 }s[maxm];
18 
19 int n,m,Mark;
20 int tp;
21 int a[maxn],b[maxn],st[maxn];
22 int Turn[maxn];
23 
24 int judge(int t){
25     for(int i=1;i<=Mark;i++)
26         a[i]=b[i];
27     for(int i=1;i<=m;i++){
28         if(s[i].l<=t && s[i].r>=t)
29             for(int j=s[i].l;j<=s[i].r;j++)
30                 a[j]--;
31     }
32     int MAX=a[1],MIN=a[1];
33     for(int i=2;i<=Mark;i++){
34         MAX=MAX>a[i]?MAX:a[i];
35         MIN=MIN<a[i]?MIN:a[i];
36     }
37     return MAX-MIN;
38 }
39 
40 int main(){
41 #ifndef ONLINE_JUDGE
42     freopen("x.in","r",stdin);
43 #endif
44 
45     scanf("%d%d",&n,&m);
46     for(int i=1;i<=n;i++)
47         scanf("%d",&a[i]);
48     for(int i=1;i<=m;i++){
49         scanf("%d%d",&s[i].l,&s[i].r);
50         st[++tp]=s[i].l,st[++tp]=s[i].r;
51     }
52     sort(st+1,st+tp+1);
53     st[0]=0;st[tp+1]=n+1;
54     for(int i=1;i<=tp+1;i++){
55         if(st[i]==st[i-1]) Turn[st[i]]=Mark;
56         else if(st[i-1]+1==st[i]) b[++Mark]=a[st[i]],Turn[st[i]]=Mark;
57         else{
58             int Max=a[st[i-1]+1],Min=a[st[i-1]+1];
59             for(int j=st[i-1]+2;j<st[i];j++){
60                 Max=Max>a[j]?Max:a[j];
61                 Min=Min<a[j]?Min:a[j];
62             }
63             if(Max==Min)
64                 b[++Mark]=Max;
65             else
66                 b[++Mark]=Max,b[++Mark]=Min;
67             b[++Mark]=a[st[i]];
68             Turn[st[i]]=Mark;
69         }
70     }
71     Mark--;
72     for(int i=1;i<=m;i++){
73         s[i].l=Turn[s[i].l];
74         s[i].r=Turn[s[i].r];
75     }
76     
77     int ans=0,ansi=0;
78     for(int i=1;i<=n;i++){
79         int t=judge(i);
80         if(t>ans)
81             ans=t,ansi=i;
82     }
83     tp=0;
84     printf("%d\n",ans);
85     for(int i=1;i<=m;i++)
86         if(s[i].l<=ansi && s[i].r>=ansi)
87             st[++tp]=i;
88     printf("%d\n",tp);
89     for(int i=1;i<=tp;i++)
90         printf("%d ",st[i]);
91     
92     return 0;
93 }
View Code

 

F. MST Unification

 題目大意:就是給你一個圖,讓你找一個最小生成樹,可是最小生成樹可能不惟一,因此你須要把某些邊的值加一些權值,你如今能夠每次給一條邊+1,問你最後你最少須要加多少次才能使得這個最小生成樹惟一。

感受就是在kruskal的時候,你把全部邊權相同的邊先都找到,而後看看哪些是能鏈接兩個以前未聯通的塊的,那麼這些邊都是可能選的,那麼你就儘量地用到他們,可是可能會有多的,那麼這些邊就都要加一,加完以後,他們就不會構成最小生成樹了,具體實現還挺簡單的。時間複雜度和kruskal同樣也是O(mlogm)

 1 /*
 2   File :
 3   Author : Robert_Yuan
 4   Date : 2019/1/29
 5   Discription :
 6 */
 7 #include<cstdio>
 8 #include<algorithm>
 9 
10 using namespace std;
11 
12 const int maxn=200010;
13 
14 struct Node{
15     int u,v,w;
16 }e[maxn];
17 
18 int n,m,ans;
19 int p[maxn];
20 
21 int Find(int x){
22     int r=x,pre;
23     while(r!=p[r]) r=p[r];
24     while(x!=r)
25         pre=p[x],p[x]=r,x=pre;
26     return r;
27 }
28 
29 bool cmp(const Node &A,const Node &B){
30     return A.w<B.w;
31 }
32 
33 int main(){
34 #ifndef ONLINE_JUDGE
35     freopen("x.in","r",stdin);
36 #endif
37     scanf("%d%d",&n,&m);
38     for(int i=1;i<=n;i++) p[i]=i;
39     for(int i=1;i<=m;i++)
40         scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
41     sort(e+1,e+m+1,cmp);
42     for(int i=1,j=1;i<=m;i=j){
43         while(j<=m && e[j].w==e[i].w) j++;
44         int cnt=j-i;
45         for(int t=i;t<j;t++){
46             int fx=Find(e[t].u),fy=Find(e[t].v);
47             if(fx==fy) cnt--; 
48         }
49         for(int t=i;t<j;t++){
50             int fx=Find(e[t].u),fy=Find(e[t].v);
51             if(fx==fy) continue;
52             cnt--;
53             p[fx]=fy;
54         }
55         ans+=cnt;
56     }
57     printf("%d",ans);
58     return 0;
59 }
View Code

 

而後div3就作完啦,感受確實比div2還要簡單一點的,以後可能還會再刷刷題哈嗯嗯

相關文章
相關標籤/搜索