Educational Codeforces Round 81 (Rated for Div. 2)
题目链接:https://codeforces.com/contest/1295/problem/A
A题~ 水题一枚~
题意:电子表上显示每个数字的屏幕由七块组成,(如图所示现在给你一个数字n,代表有n块可用的区域,问你可以显示的最大数字是多少?
题解:
如图,为显示数字0~9所需要的显示屏的数量。
用有限的显示屏显示最大的数字,那么就要保证 条件1.数字的位数最大(位数大数字一定大)条件2.相同位数的情况下,开头的数字尽可能大。
不难发现,显示数字1所需要的显示屏数量最少,所以尽可能的多显示数字1以满足条件1。
在满足上述条件的前提下,剩余的显示屏的数量必为1或0,如果剩下一个就把第一个数字1变成7即可,以满足条件2。
(实际上只会出现1和7两个数字
AC代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main(){ 4 5 int t; 6 cin>>t; 7 8 while(t--){ 9 10 int n; 11 cin>>n; 12 13 int p=n%2; 14 n/=2; 15 16 for(int i=1;i<=p;i++){ 17 cout<<7; 18 } 19 20 for(int i=p+1;i<=n;i++){ 21 cout<<1; 22 } 23 cout<<endl; 24 25 } 26 27 return 0; 28 29 }
题目链接:https://codeforces.com/contest/1295/problem/B
B题~ Orz
题意:给出一个长度为n的只含01的字符串s,字符串t可以由任意多个s组成,即t=ssss....(最后一个s可以只取前面的一部分),字符串中的01带有权值,'0'计为+1,'1'计为-1,每一位对应的值为权值的前缀和。给出一个数字x,问使得权值等于x的t有几个(无限个则输出-1)?
题解:
一开始看到题目的时候很懵逼,感觉无从下手却又有迹可循?
感觉像是一个前缀和的题目?
实际上确实和前缀和有点关系。
字符串t是一个由无限个s组成的字符串,t的每一位也会有自己对应的权值(也有相应的前缀和),我们只需要找到在这个序列中,有几个位置的前缀和等于x即可。
对于样例1
字符串s:010010,对应的前缀和为101212,t对应的前缀和则为101212 323434 545656 ........... 9 8 9 10 9 10 11 10 11 12 11 12 ......... 不难发现有3个位置前缀和为10。
可以发现t中的前缀和有规律可循,即a[i]=a[i-n]+a[n],(i>n)。可以通过一个简单的递推式求出后续每项的前缀和。
但是,如果逐项推下去依次检索(即暴搜)必会TLE,就要想办法进行优化。
通过上述递推式,可以推出,如果某个位置的前缀和a[i]==x,(i>0)则会有对应的,a[j]+k*x==x(j>n)。
也就是说,只要检验从1~n项的前缀和a[j]加k*a[n]是否等于x,是否有满足条件的k(k必须>=0)存在,如果存在,则会有一个对应数字k使第(i+k*n)项前缀和等于x,如果不存在则后续所有的第(i+k*n)项都不满足。
如何检验呢?
这里我使用了一种比较神奇的方法(就是瞎搞搞推出来的,推理过程不赘述)——取模。
如果满足条件,x一定比a[j]多k个a[n],那么只要x和a[j]模a[n]的结果相同(即把x去掉k个a[n]),即成立。
但是这样取模会出现正负数之间取模、和模0的问题,所以,这里采用a[j]和x同号则绝对值相减(差也取绝对值以保证为正数)取模a[n]的绝对值,异号则绝对值相加取模a[n]的绝对值,如果结果为零则成立。同时要判断k>=0是否成立,如果a[j]>x且a[n]>0或者a[j]<x且a[n]<0,此时k<0,不成立。对有无限个解的情况进行特判,即a[n]==0且存在前缀和a[j]==x,直接输出-1,如果a[n]==0且不存在a[j]==x,那么无解输出0。
AC代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 long long a[100001]; 5 6 long long absj(long long a,long long b){ 7 return abs(abs(a)-abs(b)); 8 } 9 10 long long absp(long long a,long long b){ 11 return abs(abs(a)+abs(b)); 12 } 13 14 int main(){ 15 16 int t; 17 cin>>t; 18 19 while(t--){ 20 21 long long n,x; 22 cin>>n>>x; 23 24 string s; 25 cin>>s; 26 27 s='%'+s; 28 29 a[0]=0; 30 bool flag=0; 31 32 for(int i=1;i<=n;i++){ 33 34 if(s[i]=='1'){ 35 a[i]=a[i-1]-1; 36 } 37 else{ 38 a[i]=a[i-1]+1; 39 } 40 41 if(x==a[i]){ 42 flag=1; 43 } 44 45 } 46 47 if(a[n]==0){ 48 if(flag==1) cout<<-1<<endl; 49 else cout<<0<<endl; 50 continue; 51 } 52 53 long long ans=0; 54 55 if(x==0){ 56 ans++; 57 } 58 59 for(int i=1;i<=n;i++){ 60 61 if((a[i]>x&&a[n]>0)||(a[i]<x&&a[n]<0)){ 62 continue; 63 } 64 65 if(a[i]*x>=0&&absj(a[i],x)%abs(a[n])==0){ 66 ans++; 67 } 68 if(a[i]*x<0&&absp(a[i],x)%abs(a[n])==0){ 69 ans++; 70 } 71 72 } 73 74 cout<<ans<<endl; 75 76 } 77 78 return 0; 79 80 }
(Orz,在线投诉老学姐压榨学弟写题!!!!)
题目链接:https://codeforces.com/contest/1295/problem/C
C题
题意:给出字符串s和字符串t,以及一个空字符串z,将s的某个子串加入z的末端计为一次操作,至少多少次操作可以使z和t相同?(无法完成则输出-1)
题解:
思路:暴力贪心+优化!
题目中问至少需要多少次操作,也就是尽可能少的分割t使其分割后的各部分为s的子串。
最初的思路是,依次在s中查找t中的每个字符,如果出现t[i]==s[j],则i++(即从当前位置继续搜索t的下一位),将s中的字符全部访问一遍后计为一次操作。这样就是从t的头部依次寻找最长的s的子串。
复杂度最大为1e10,显然TLE。
优化:开一个vector<int> v[26]数组,把s中每个字母出现的位置依次存入数组,v[i]中存着对应字母出现的所有位置。遍历字符串t,查找t中字母在v[t[i]-'a']中大于前一个字母(t[i-1])出现的位置的最小值并更新当前位置,如果不存在这样的位置则操作数+1,并将当前位置重置为0。
在vector数组中查找的过程中使用二分优化查找以减少时间复杂度。
AC代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 vector<int> v[26]; 5 6 void init(){ 7 8 for(int i=0;i<=25;i++){ 9 v[i].clear(); 10 } 11 12 } 13 14 int main(){ 15 16 int t; 17 cin>>t; 18 while(t--){ 19 20 init(); 21 22 string s,t; 23 cin>>s>>t; 24 25 int ls=s.length(),lt=t.length(); 26 s='%'+s,t='%'+t; 27 28 for(int i=1;i<=ls;i++){ 29 v[s[i]-'a'].push_back(i); 30 } 31 32 int l=1,r=1,ans=1; 33 bool ff=1; 34 35 int M=0; 36 37 while(l<=lt){ 38 39 int p=t[l]-'a'; 40 41 if(!v[p].size()){ 42 ff=0; 43 break; 44 } 45 46 if(v[p][v[p].size()-1]<=M){ 47 ans++; 48 M=0; 49 continue; 50 } 51 52 int ll=0,rr=v[p].size()-1; 53 54 /* for(int j=0;j<v[p].size();j++){ //TLE 12 55 if(v[p][j]>M){ 56 M=v[p][j]; 57 l++; 58 break; 59 } 60 }*/ 61 62 while(ll<rr){ //二分优化 63 64 int mid=(ll+rr)>>1; 65 66 if(v[p][mid]>M){ 67 rr=mid; 68 } 69 else{ 70 ll=mid+1; 71 } 72 73 } 74 75 M=v[p][ll],l++; 76 77 } 78 79 if(!ff){ 80 cout<<-1<<endl; 81 } 82 else{ 83 cout<<ans<<endl; 84 } 85 86 } 87 88 return 0; 89 90 }
摸过的🐟,划过的水,早晚要还的~(暗示ljxfp赶紧来写题)