Codeforces Round #604 (Div. 2)
A. Beautiful String (CF 1265 A)
题目大意
当没有连续两个字母相同时,该字符串为美丽串,给定一个含的字符串,要求将替换成,或,使得该串为美丽串。若无法成为美丽串输出
解题思路
很显然对于每个的取值只跟它左右两个位置有关,而它一定能取到一个合法的值,只要跟左右不同即可,而如果一开始就有连续两个字母相同则输出.
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } bool qwq; char s[100500]; void check(int x){ s[x]='a'; if (x==0){ if (s[x+1]=='a') s[x]='b'; } else{ if (s[x-1]=='a'||s[x+1]=='a') { s[x]='b'; if (s[x-1]=='b'||s[x+1]=='b') { s[x]='c'; if (s[x-1]=='c'||s[x+1]=='c') qwq=false; } } } } void Input(void) { scanf("%s",s); int len=strlen(s); qwq=true; for(int i=0;i<len;++i){ if (qwq==false) break; if (i!=0&&s[i]==s[i-1]) qwq=false; if (s[i]=='?') check(i); } if (!qwq) printf("-1\n"); else printf("%s\n",s); } void Solve(void) {} void Output(void) {} main(void) { int kase; freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); read(kase); for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); } }
B. Beautiful Numbers (CF 1265 B)
题目大意
给定一个排列,求每一个,是否存在使得区间是~的一个全排列,存在则为美丽数字。输出一个数字串,第个数字表示是否是美丽数字,是则,否则
解题思路
的时候我们选择的位置,然后我们从的位置进行拓展,很显然我们每一次贪心的向左右两个数中较小的那个数进行扩展,才有可能得到一个~的全排列。
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int n,l,r,pos,cnt,mi,ma; const int N=2e5+8; int p[N]; void Input(void) { read(n); for(int i=1;i<=n;++i) { read(p[i]); if (p[i]==1) pos=i; } } void Solve(void) { p[0]=p[n+1]=3e6; l=r=pos; cnt=1; mi=ma=1; putchar('1'); while(cnt<n){ if (l==1){ ++r; ++cnt; ma=MAX(ma,p[r]); }else if (r==n){ --l; ++cnt; ma=MAX(ma,p[l]); }else if (p[l-1]>p[r+1]) { ++r; ++cnt; ma=MAX(ma,p[r]); } else{ --l; ++cnt; ma=MAX(ma,p[l]); } if (cnt==ma-mi+1) putchar('1'); else putchar('0'); } puts(""); } void Output(void) {} main(void) { int kase; freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); read(kase); for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); } }
C. Beautiful Regional Contest (CF 1265 C)
题目大意
个选手参加比赛,递减给出它们解决的题数,分配个金牌个银牌和个铜牌,要求且,且,且获得金牌的选手的题数严格大于获得银牌选手的题数,获得银牌的选手题数严格大于获得铜牌选手的题数,获得铜牌的选手题数严格大于获得铁牌(无牌)(来杯拿铁咖啡)选手的题数,问能否做到,若能,给定一个可行的分配方案,使得发牌数最大。
解题思路
题目对于有大小限制而和之间没有,则我们让尽可能小就好了,即选解题数最高的那一组选手全部发金牌,然后发银牌,直到,剩下的选手全部发铜牌,直到发的牌数恰好小于即可,做不到则不可行。
如果你是良心比赛方可以最后把和调换一下。
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int n,g,s,b,qwq,re; const int N=4e5+8; int num[N],cnt[N]; void Input(void) { qwq=0; num[0]=-1; read(n); for(int a,i=1;i<=n;++i) { read(a); if (num[qwq]!=a) num[++qwq]=a; ++cnt[qwq]; } s=g=b=re=0; } void Solve(void) { g=cnt[1]; int i=2; while(s<=g) s+=cnt[i++]; while(b<=g) b+=cnt[i++]; if (s+g+b>n/2) g=s=b=0; else {while(s+g+b<=n/2) b+=cnt[i++]; b-=cnt[--i];} printf("%d %d %d\n",g,s,b); for(int i=1;i<=n;++i) num[i]=cnt[i]=0; } void Output(void) {} main(void) { int kase; freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); read(kase); for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); } }
D. Beautiful Sequence (CF 1265 D)
题目大意
给了个,个,个,个,要求排成一个序列,使得俩俩差值为,输出任意符合要求序列即可,没有则
解题思路
- 如果有三个数是个,则第四个数数量为则可行,否则不可行。
- 如果有两个数是个,则看另外两个数的差值是否为,是则由植树原理再判断二者的差值是否为一,是则可行,其余情况均不可行。
- 如果有一个数是个,则看这个数是多少,若为或则不可行,当为或时,不失一般性,假设是的数为个,那么我们排列,,,由植树原理可知如果则可行,即如此排列,若为则在序列最左边放一个,若为则在最左边和最右边各放一个即可。
- 如果有零个数是个,我们考虑和,首先且,否则不可行。我们按照,和如此排好后,考虑和之间的排列,我们如此排列,直到用光或者,此时如果还有剩余的,假设还有剩余,若剩余个,则放到最左边,否则不可行,若还有剩余,若剩余个,则放到最右边,否则不可行.
综上即可。
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int a,b,c,d,mi,cnt; bool qwq; void Input(void) { cnt=0; read(a); read(b); read(c); read(d); cnt=(a==0)+(b==0)+(c==0)+(d==0); } void check1(){ int sign=0; if (a==0) sign=0; else if (b==0) sign=1; else if (c==0) sign=2; else sign=3; if (sign==1||sign==2){ qwq=true; return; } if (sign==3){ b-=a-1; b-=c-1; --b; if (b<0||b>2){ qwq=true; return; } else{ printf("YES\n"); if (b) --b,printf("1 "); for(int i=1;i<=a;++i) printf("0 1 "); for(int i=1;i<c;++i) printf("2 1 "); printf("2"); if (b) printf(" 1\n"); else printf("\n"); return; } } else{ c-=b-1; c-=d-1; --c; if (c<0||c>2){ qwq=true; return; } else{ printf("YES\n"); if (c) --c,printf("2 "); for(int i=1;i<=b;++i) printf("1 2 "); for(int i=1;i<d;++i) printf("3 2 "); printf("3"); if (c) printf(" 2\n"); else printf("\n"); return; } } } void check2(){ int s1=-1,s2=-1; int cnt[4]; cnt[0]=a; cnt[1]=b; cnt[2]=c; cnt[3]=d; if (a!=0) s1=0; if (b!=0) if (s1==-1) s1=1;else s2=1; if (c!=0) if (s1==-1) s1=2;else s2=2; if (d!=0) if (s1==-1) s1=3;else s2=3; if (s2-s1>1) { qwq=true; return; } else{ if (ABS(cnt[s2]-cnt[s1])>1){ qwq=true; return; } else{ printf("YES\n"); if (cnt[s2]>cnt[s1]) {for(int i=1;i<=cnt[s1];++i) printf("%d %d ",s2,s1); printf("%d\n",s2);} else if (cnt[s2]<cnt[s1]) {for(int i=1;i<=cnt[s2];++i) printf("%d %d ",s1,s2); printf("%d\n",s1);} else for(int i=1;i<=cnt[s1];++i) printf("%d %d%c",s1,s2,i==cnt[s1]?'\n':' '); return; } } } void check3(){ int si=0; int cnt[4]; cnt[0]=a; cnt[1]=b; cnt[2]=c; cnt[3]=d; for(int i=0;i<4;++i) if (cnt[i]!=0) si=i; if (cnt[si]==1){ printf("YES\n"); printf("%d\n",si); return; } else { qwq=true; return; } } void Solve(void) { qwq=false; if (a>b&&(c!=0||d!=0)) qwq=true; else if (d>c&&(b!=0||a!=0)) qwq=true; if (!qwq){ if (cnt==0){ b-=a; c-=d; mi=MIN(b,c); b-=mi; c-=mi; if (b==1||c==1){ printf("YES\n"); if (b==1) { printf("1 "); for(int i=1;i<=a;++i) printf("0 1 "); for(int i=1;i<=mi;++i) printf("2 1 "); for(int i=1;i<=d;++i) printf("2 3%c",i==d?'\n':' '); } else{ for(int i=1;i<=a;++i) printf("0 1 "); for(int i=1;i<=mi;++i) printf("2 1 "); for(int i=1;i<=d;++i) printf("2 3 "); printf("2\n"); } } else if (b==c){ printf("YES\n"); for(int i=1;i<=a;++i) printf("0 1 "); for(int i=1;i<=mi;++i) printf("2 1 "); for(int i=1;i<=d;++i) printf("2 3%c",i==d?'\n':' '); } else qwq=true; } else{ if (cnt==1) check1(); else if (cnt==2) check2(); else if (cnt==3) check3(); } } if (qwq) printf("NO\n"); } void Output(void) {} main(void) { //int kase; freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); //read(kase); //for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); //} }
由于昨晚放弃思考了所以代码写了巨长……
E. Beautiful Mirrors (CF 1265 E)
题目大意
枚镜子,每天问一枚镜子,从第一枚镜子开始问是否漂亮,是则下一天跳转到下一个镜子,否则下一天从第一枚镜子重新开始问。当最后一枚镜子即第枚镜子说她漂亮时,她就变得很开心。问她变得开心的期望天数。
解题思路
期望题,我们通常逆推,即从结果推到初始状态。
设表示从第枚镜子开始问,变得开心(即问到第n枚镜子得到漂亮回答)的期望天数。
根据期望的定义,我们有以及
即对于第天,它有的概率需要才变得开心,也有的概率需要才变得开心。
右式一共有个方程,我们可以用高斯消元即可解出答案,但复杂度是,会超时。
由于逆推状态不当,导致式子中存在,这对答案求值造成很大不便,我们需要修改状态。
由于回答不漂亮会回到第一枚镜子,那么我们希望第一枚镜子的状态会是一个已知状态。
我们设表示从第一枚镜子问,问到第枚镜子且回答漂亮的期望天数。
则,
即我们要问第枚镜子,首先需要期望天数问到第枚镜子并得到漂亮的回答,然后对于第枚镜子,我们有的概率花一天得到漂亮的回答,也有的概率得到不漂亮回答,这时需要再花天才能得到漂亮的回答,所以总共需要 (是问第枚镜子得到不漂亮的回答的那一天),化简一下即可得到,递推即可得到答案
(这个和上面是等价的,即有的概率得到漂亮答案,这时需要的天数是,也有的概率得到了不漂亮回答,这时我们需要从第一枚镜子重新问,此时得到第枚镜子的漂亮回答的天数是)
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const long long mo=998244353; long long kuai(long long a,long long b){ int qwq=1; while(b){ if (b&1) qwq=a*qwq%mo; a=a*a%mo; b>>=1; } return qwq; } void Input(void) { int n; long long ans=0,f; read(n); for(int i=1;i<=n;++i){ read(f); ans=(ans+1)%mo*100%mo*kuai(f,mo-2)%mo; } printf("%lld\n",ans); } void Solve(void) {} void Output(void) {} main(void) { //int kase; freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); //read(kase); //for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); //} }
C. Beautiful Mirrors with queries (CF 1264 C)
题目大意
枚镜子,每天问一枚镜子,从第一枚镜子开始问是否漂亮,是则下一天跳转到下一个镜子,否则下一天从小于当前镜子编号的标有的最大编号的镜子问。当最后一枚镜子即第枚镜子说她漂亮时,她就变得很开心。
有次操作,每次操作将第枚镜子设为(如果之前不是)或者取消(如果之前是),对于每次操作,回答她变得开心的期望天数。
解题思路
由于镜子回答不漂亮的时候不再是从第一枚镜子问,相当于把连续的镜子分成了若干段,不同段之间互不干扰,对于每一段,我们都可以假想是从第一枚镜子问到第枚镜子并得到漂亮回答的期望天数,由期望的线性可加性,我们把不同段的期望天数相加即可得到答案。而每次操作仅仅增加或移除一个,如果我们能够快速知道某段区间对答案贡献的期望天数,即可快速解决此题。
上一题中一开始的递推式,某人通过解前几项的规律得出,这是我们从号镜子开始问问到第号镜子并得到漂亮回答的答案。其实这个从后来的递推式展开也可得到。
这样子的话,对于一个新添加的,记为,再设编号小于编号的最大编号为,编号大于编号的最小编号为,则从到的期望天数,从到的期望天数并没有受到影响,而从到的期望天数被分成了两部分,一部分是从到,一部分是到,我们只要减去到的期望贡献,再加上到和到的期望贡献即可得到新的答案,移除的话即减去到和到的期望贡献,加回到的期望贡献即可。
从上面展开递推式的过程(这里没有)我们可以知道,对于从到都得到漂亮回答的期望天数为,由此我们只要预处理以及,那么对于从到都得到漂亮回答的情况,,,二者相除即是期望天数.
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int n,q; const int N=2e5+8; const long long mo=998244353; long long psum[N],pmul[N],ipmul[N],p[N]; long long ans; set<long long> mirror; long long kuai(long long a,long long b){ long long qwq=1; while(b){ if (b&1) qwq=qwq*a%mo; a=a*a%mo; b>>=1; } return qwq; } void Input(void) { read(n); read(q); long long tmp=kuai(100,mo-2); for(int i=1;i<=n;++i){ read(p[i]); p[i]=p[i]*tmp%mo; } } long long getans(long long l,long long r){ long long qwq=0; if (l==1) qwq=0; else qwq=psum[l-2]; return (pmul[l-1]*(psum[r-1]-qwq)%mo*ipmul[r]%mo*ipmul[l-1])%mo; } void Solve(void) { pmul[0]=1; ipmul[0]=1; psum[0]=1; for(int i=1;i<=n;++i){ pmul[i]=pmul[i-1]*p[i]%mo; psum[i]=(psum[i-1]+pmul[i])%mo; ipmul[i]=kuai(pmul[i],mo-2); } ans=getans(1,n); mirror.insert(1); mirror.insert(n+1); for(int u,i=1;i<=q;++i){ read(u); if (mirror.count(u)){ auto mid=mirror.find(u); auto l=mid,r=mid; --l; ++r; ans-=getans(*l,*mid-1); ans-=getans(*mid,*r-1); ans+=getans(*l,*r-1); mirror.erase(u); } else{ auto mid=mirror.insert(u).first; auto l=mid,r=mid; --l; ++r; ans+=getans(*l,*mid-1); ans+=getans(*mid,*r-1); ans-=getans(*l,*r-1); } ans%=mo; while(ans<0) ans+=mo; printf("%lld\n",ans); } } void Output(void) {} main(void) { //int kase; freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); //read(kase); //for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); //} }
F. Beautiful Bracket Sequence (easy version)
题目大意
定义一个合法的括号串以及它的深度:
- 合法的括号串即左括号数量等于右括号数量且对于任意一个左括号都能找到一个与之匹配的右括号,
- 空括号串的深度为
- 当一个合法括号串的深度为,则括号串的深度为
- 如果有两个合法的括号串和,则括号串的深度为两者中的较大值
则对于一个(可能不合法)的括号串,它的深度为它所有合法括号子串(可以不连续)深度的最大值。
现给定一个含有"(" "?" 和")"括号串,对"?"取所有值("("或")"),求所有情况该括号串深度和。
解题思路
我们先解决如何计算一个括号串的深度。
这是个区间问题,我们定义表示的深度
如果 且 或者其中有一个或两个是,那么
否则
这就解决了深度的问题。
从中我们考虑每对括号对答案的贡献,即设表示中的括号取遍所有情况,该串的深度和。
当时,此时和对答案没有任何贡献,那么
当时,此时和对答案没有任何贡献,那么
注意到如果且,我们加上了两次的贡献,此时要
而如果 且 或者其中有一个或两个是,即且,这个时候和可以形成一对括号,对答案贡献了1,若区间有个问号,则有种情况,此时对答案贡献为,故
综上,即:
- ,
- ,
- && ,
- 即 && ,
区间DP。
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const long long mo=998244353; const int N=2e3+8; char s[N]; long long sum[N],dp[N][N]; int len; void Input(void) { scanf("%s",s); } long long kuai(long long a,long long b){ long long qwq=1; while(b){ if (b&1) qwq=qwq*a%mo; a=a*a%mo; b>>=1; } return qwq; } void Solve(void) { len=strlen(s); sum[0]=(s[0]=='?'); for(int i=1;i<len;++i) sum[i]=sum[i-1]+(s[i]=='?'); for(int l=2;l<=len;++l) for(int j,i=0;i<len-l+1;++i){ j=i+l-1; if (s[i]!='(') dp[i][j]=(dp[i][j]+dp[i+1][j])%mo; if (s[j]!=')') dp[i][j]=(dp[i][j]+dp[i][j-1])%mo; if (s[i]!='('&&s[j]!=')') dp[i][j]=(dp[i][j]-dp[i+1][j-1]+mo)%mo; if (s[i]!=')'&&s[j]!='(') dp[i][j]=((dp[i][j]+dp[i+1][j-1])%mo+kuai(2ll,sum[j-1]-sum[i]))%mo; } } void Output(void) { printf("%lld\n",dp[0][len-1]); } main(void) { //int kase; freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); //read(kase); //for (int i = 1; i <= kase; i++) { //printf("Case #%d: ", i); Input(); Solve(); Output(); //} }
这次的题都是贪心构造和期望和DPqwq。
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/11995096.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步