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 }
而后交上去,刷新,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 }
而后又是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 }
这题我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 }
这题由于实在经典,因此彷佛没有以前获得的乐趣那么多了。不过能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 }
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 }
题目大意:就是给你一个图,让你找一个最小生成树,可是最小生成树可能不惟一,因此你须要把某些边的值加一些权值,你如今能够每次给一条边+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 }
而后div3就作完啦,感受确实比div2还要简单一点的,以后可能还会再刷刷题哈嗯嗯