Codeforces Global Round 7
A. Bad Ugly Numbers
题意:找一个n位的正数,使得它的每一位都不能整除这个数。
思路:构造2333...这样的序列即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 using namespace std; 17 int T,n; 18 int main(){ 19 // freopen("in.txt","r",stdin); 20 rd(T); 21 while(T--){ 22 rd(n); 23 if(n == 1)printf("-1\n"); 24 else { 25 printf("2"); 26 for(int i=1;i<n;i++)printf("3"); 27 puts(""); 28 } 29 } 30 return 0; 31 } 32 /**/
B. Maximums
题意:对一个长n(2e5)的序列ai,构造一个新序列bi=ai-max(0,a1,a2,...,ai-1),现给出bi,让你构造一个ai出来。
思路:a1可以直接求出来,就是b1,然后a2也可以求出来,因为我们知道a1和b2,同理ai也可以求出来,因为我们可以知道a1,a2,..,ai-1和bi。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=2e5+10; 17 using namespace std; 18 int n,b[N],a[N]; 19 int main(){ 20 // freopen("in.txt","r",stdin); 21 rd(n);for(int i=1;i<=n;i++)rd(b[i]); 22 a[1]=b[1];int mx=a[1]; 23 for(int i=2;i<=n;i++){ 24 a[i]=b[i]+mx;mx=max(mx,a[i]); 25 } 26 for(int i=1;i<=n;i++)printf("%d ",a[i]);puts(""); 27 return 0; 28 } 29 /**/
C. Permutation Partitions
题意:给一个n(2e5)的排列,将他划分为k段,每段权值为该段最大值,记录这种划分方式的权值为每段权值之和,求使得最终权值最大的划分方式有多少种,权值是多少。
思路:我们要让前k大的数不出现在同一段中,这样可以使得最终的权值最大,接下来只需要记录两个前k大的数字中间断点的可能位置数目即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int mod=998244353; 17 const int N=2e5+10; 18 using namespace std; 19 int n,k; 20 int p[N]; 21 int main(){ 22 // freopen("in.txt","r",stdin); 23 rd(n);rd(k);for(int i=1;i<=n;i++)rd(p[i]); 24 LL ans1=1ll*(2*n-k+1)*k/2;int ans2=1; 25 int lst=0; 26 for(int i=1;i<=n;i++){ 27 if(p[i] >= n-k+1){ 28 if(lst)ans2=1ll*ans2*(i-lst)%mod; 29 lst=i; 30 } 31 } 32 printf("%lld %d\n",ans1,ans2); 33 return 0; 34 } 35 /**/
D2. Prefix-Suffix Palindrome (Hard version)
题意:给一个长n(1e6)的字符串,找一个它的前缀和后缀组成一个新的字符串使得该字符串是回文串,求最长的回文串。
思路:我们发现最终字符串一定是由原字符串的长度相等的前缀后缀(二者能构成回文串)以及剩下部分的一个回文前缀或是后缀构成,我们不妨设前缀为a,后缀为b,剩下的回文子串为c。用马拉车处理出len数组。我们枚举c的中心,可以发现,c尽可能取长结果总不会变差,因为如果本来不可行,取短后也不可行,本来可行,取短后最多带来的也只是a和b的长度各增加1,最终长度不会变优。所以枚举c的中心,如果他在前半部分,那么他是作为"前缀"出现,如果出现在后半部分,就是作为"后缀"出现,我们只需要看对应位置所找到的a,b能否满足ab构成回文串即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=1e6+10; 17 using namespace std; 18 char s[N],s2[N<<1]; 19 int len[N<<1]; 20 int n; 21 void Manachar(){ 22 for(int i=1;i<=n;i++)s2[i*2]=s[i],s2[i*2-1]='%'; 23 s2[n*2+1]='%';n=n*2+1; 24 int p=0,id=0; 25 for(int i=1;i<=n;i++){ 26 if(i<=p)len[i]=min(p-i+1,len[id*2-i]); 27 else len[i]=1; 28 while(i+len[i]<=n && i-len[i]>=1 && s2[i+len[i]]==s2[i-len[i]])len[i]++; 29 if(len[i]+i-1>p)p=i+len[i]-1,id=i; 30 } 31 } 32 int T; 33 bool is[N<<1]; 34 int ans_len,ans_id; 35 int main(){ 36 // freopen("in.txt","r",stdin); 37 rd(T); 38 while(T--){ 39 scanf("%s",s+1);n=strlen(s+1); 40 Manachar();for(int i=1;i<=n;i++)is[i]=0; 41 for(int l=1,r=n;l<=r;l++,r--){ 42 if(s2[l] != s2[r])break; 43 is[l]=1;is[r]=1; 44 }is[0]=is[n+1]=1; 45 ans_len=0; 46 for(int i=1;i<=n/2;i++){ 47 if(!is[i-len[i]])continue; 48 int now_len=i-1; 49 if(now_len > ans_len)ans_len=now_len,ans_id=i; 50 } 51 for(int i=n/2+1;i<=n;i++){ 52 if(!is[i+len[i]])continue; 53 int now_len=n-i; 54 if(now_len > ans_len)ans_len=now_len,ans_id=i; 55 } 56 if(ans_id <= n/2){ 57 for(int i=2;i<=ans_id+len[ans_id]-1;i+=2)printf("%c",s2[i]); 58 for(int i=ans_id-len[ans_id];i>=1;i-=2)printf("%c",s2[i]); 59 puts(""); 60 } 61 else { 62 for(int i=n-1;i>=ans_id+len[ans_id];i-=2)printf("%c",s2[i]); 63 for(int i=ans_id-len[ans_id]+2;i<=n;i+=2)printf("%c",s2[i]); 64 puts(""); 65 } 66 } 67 return 0; 68 } 69 /**/
反思:这个题为什么考场没做出来呢?因为我强行联系了之前的一道好题。Codeforces Round #619 (Div. 2)E. Nanosoft。我当时似乎对这道题的处理方法给了一个应用场景,就是你需要求一段数中每个数的权值和其位置与边界作差得到的数字取较小值后,再取最大值。这道题似乎也是这样的,对于每个符合条件的ab,求出剩下子串中c的长度,而c的长度需要用len数组以及c的位置所确定,但是细想并不是(其实不细想也不是,考场高估了题目难度),这样应用是有问题的,因为他要求c必须是一个前缀或者后缀,而上述做法无法满足这一条件。本质还是对题目性质发掘的不够所造成的。
E. Bombs
题意:给一个长度为n(3e5)的排列pi,定义一种运算:给某些位置打上标记,然后从依次将排列中的数放到一个容器中,一旦遇到了带有标记的位置,便删去容器中最大的那个数字,最终得到的容器中最大的数字便是这种标记方法对应的答案。现在再给出一个排列qi,输出n个数字,第i个数字表示为p中第q1,q2...q(i-1)个数字打上标记后对应的答案。
思路:可以发现,如果当前答案<=k,那么对于每个位置,它及它右面所有数字中大于k的数字的个数一定小于等于被标记的个数,否则删不干净,一定会出现>k的值在最终的容器中。我们还可以发现,输出的值是不增的,因为在原有基础上增加tag不会使得原本被删除的数恢复,只会删除更多的数。k从n开始,我们记录每个位置上"当前位置及其右面所有大于k的数字的个数-当前位置及其右面被标记的数的个数",如果所有数都<=0了,就说明最终答案是<=当前的k的,k--,我们只需要找到第一个存在>0的情况对应的k,k+1就是当前对应的答案。我们用一颗维护最大值的线段树来确定是否区间所有数字都<=0,而每次增加标记的操作可以变为区间-1的操作,每次更改k的操作可以变为区间+1的操作。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=3e5+10; 17 using namespace std; 18 int n,p[N],hs[N]; 19 #define ls (o<<1) 20 #define rs (o<<1|1) 21 #define mid (l+r>>1) 22 int f[N<<2],tag[N<<2]; 23 void tagdown(int o){f[ls]+=tag[o];f[rs]+=tag[o];tag[ls]+=tag[o];tag[rs]+=tag[o];tag[o]=0;} 24 void add(int x,int y,int o=1,int l=1,int r=n){ 25 if(r <= x){f[o]+=y;tag[o]+=y;return ;} 26 tagdown(o);if(x > mid)add(x,y,rs,mid+1,r); 27 add(x,y,ls,l,mid);f[o]=max(f[ls],f[rs]); 28 } 29 int main(){ 30 // freopen("in.txt","r",stdin); 31 rd(n);for(int i=1;i<=n;i++)rd(p[i]),hs[p[i]]=i; 32 int now=n; 33 for(int i=1;i<=n;i++){ 34 int x;rd(x); 35 while(now > 0 && f[1] <= 0)add(hs[now--],1); 36 printf("%d ",now+1);add(x,-1); 37 } 38 return 0; 39 } 40 /**/
反思:这种思路很巧妙,我本来在考虑每次标记对应的删除操作如何根据之前已有的删除操作得到需要删除的数字,然而没有结果。