Codeforces Round #517 (Div. 2, based on Technocup 2019 Elimination Round 2)
总结: 最近的cf,或者说cf很多时候是个手速场,它需要你能在一定时间内注意力高度集中去看一些问题或者去思考一些事!
其实大部分东西都是没有见过的,你能做到只是静下心来思考发现问题!
A题:Golden Plate
简单题,直接看一下模拟而已!
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <bitset> 5 #include <algorithm> 6 #include <iostream> 7 typedef long long ll; 8 using namespace std; 9 // 2*(7+9)=2*16 -4 10 int n,m,k; 11 int main(){ 12 scanf("%d%d%d",&n,&m,&k); 13 int ans=0; 14 for(int i=1;i<=k;i++){ 15 if(i==1){ 16 ans=2*(n+m)-4; 17 }else{ 18 ans=ans+(2*(n-4*(i-1)+m-4*(i-1))-4); 19 } 20 } 21 printf("%d\n",ans); 22 return 0; 23 }
这个还是能写的很快的,只要能一下子想清楚后再写!!!!!!!!
————————————————————————————————————————————————————
写想清楚每次变化变化多少,然后循环怎么写
然后就一下子就可以写好了
Think Twice ,Code Once
————————————————————————————————————————————————————
这个题开始卡了挺久的,主要卡的原因是我一开始没想清楚,看错题了,看成了异或,然后我就写成了异或,最后发现
不太对,然后后来改着改着才改过来的!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
我们首先想他们给我个4是为什么??????????????????????????????????????
4的话,我们就就可以枚举l[1]的值,枚举为0到3,然后如果l[1]的值我们知道了的话,那么由于异或和这个的性质我们第
2个数也可以很好的得出来了,这个数是唯一的,我们还可以这样想这个比较小,我们又可以继续枚举,直接就可以搞了
然后就是3重循环
第一个循环是枚举a[1],第二个是从2到n,第三个是枚举0到3,即a[i]的值,如果验证可以的话
就输出。
下面是代码:
还是很好写的,主要是要第一遍想清楚
—————————————————————————————————————————————————————
最重要的是Think Twice,Code Once!
—————————————————————————————————————————————————————
下面是代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <bitset> 5 #include <algorithm> 6 #include <iostream> 7 typedef long long ll; 8 using namespace std; 9 const int maxn=(int)(1e5+100); 10 // 2*(7+9)=2*16 -4 11 int n; 12 int ai[maxn],bi[maxn],ti[maxn]; 13 int a[maxn][2],b[maxn][2]; 14 int l1,l2; 15 int main(){ 16 scanf("%d",&n); 17 for(int i=1;i<n;i++){ scanf("%d",&ai[i]); } 18 for(int i=1;i<n;i++){ scanf("%d",&bi[i]); } 19 for(int i=1;i<n;i++){ 20 for(int j=0;j<=1;j++){ 21 if(((bi[i]>>j)&1)!=0){ 22 if(((ai[i]>>j)&1)==0){ 23 printf("NO\n"); 24 return 0; 25 } 26 } 27 } 28 } 29 int flag=1; 30 for(int i=0;i<=3;i++){ 31 ti[1]=i; 32 flag=1; 33 for(int j=2;j<=n;j++){ 34 int res=0; 35 for(int k=0;k<=3;k++){ 36 ll s=ti[j-1]|k,e=ti[j-1]&k; 37 if(s==ai[j-1]&&e==bi[j-1]){ 38 res=1; 39 ti[j]=k; 40 break; 41 } 42 } 43 if(res==0){flag=0;break;} 44 } 45 if(flag==1){ 46 printf("YES\n"); 47 for(int j=1;j<=n;j++){ 48 if(j==n) printf("%d\n",ti[j]); 49 else printf("%d ",ti[j]); 50 } 51 return 0; 52 } 53 } 54 // 3|2=3 3&2= 55 printf("NO\n"); 56 return 0; 57 }
——————————————————————————————————————————————————————
C题:Cram Time
这个也是个很好证明的东东,只要找到第一个大于n+m的k*(k+1)/2就ok了 ,然后把k--
只要这样分配了后我们就肯定能把这个分配完!!!!!!!!!!!!!!!!!!!
我们把前面小的给他分配掉,这样肯定是最优的。
第一个我们把小的给他,然后如果有塞出来的空,我们就把那个空给补上,然后其他都给第二个!
感觉这个check挺好写的,但是我写的有点狗,这个要想清楚点。。。。
下面是代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <bitset> 5 #include <algorithm> 6 #include <iostream> 7 typedef long long ll; 8 using namespace std; 9 const int maxn=(int)(1e5+100); 10 ll n,m,a,b; 11 ll s1[maxn],s2[maxn]; 12 13 ll check(ll mid){ 14 ll res=mid*(mid+1); 15 res=res/2; if(res>(a+b)) return false; 16 if(a==0){ 17 n=0; m=mid; 18 for(int j=1;j<=mid;j++) s2[j]=j; 19 return true; 20 } 21 //a>0 22 // 先考虑塞满a 1 2 3 .... i a-sum<i+1; 23 ll i=1,sum=1; 24 for(;;){ 25 if((i*(i+1))/2>a) break; 26 i++; 27 }i--;sum=(i*(i+1))/2; 28 if(a==sum){ 29 n=i;m=mid-i; 30 for(int j=1;j<=i;j++) s1[j]=j; 31 for(ll j=i+1;j<=mid;j++) s2[j-i]=j; 32 return true; 33 } 34 35 // s=sum+(i+1)-a; 36 n=i;m=mid-i; int q1=0,q2=0; 37 for(int j=1;j<=i+1;j++){ 38 if(j==(sum+(i+1)-a)) continue; 39 s1[++q1]=j; 40 } s2[++q2]=sum+(i+1)-a; 41 for(ll j=i+2;j<=mid;j++){ 42 s2[++q2]=j; 43 } 44 return true; 45 } 46 47 int main(){ 48 scanf("%lld%lld",&a,&b); 49 if((a+b)==0){ 50 printf("0\n"); printf("\n"); 51 printf("0\n"); printf("\n"); 52 return 0; 53 } 54 ll lb=1,rb=(ll)(1e5+100),ans=1; 55 while(lb<=rb){ 56 ll mid=(lb+rb)/2; 57 if(check(mid)){ 58 ans=mid; 59 lb=mid+1; 60 }else{ 61 rb=mid-1; 62 } 63 } 64 printf("%lld\n",n); 65 for(int i=1;i<=n;i++) printf("%lld ",s1[i]); printf("\n"); 66 printf("%lld\n",m); 67 for(int i=1;i<=m;i++) printf("%lld ",s2[i]); printf("\n"); 68 return 0; 69 }
—————————————————————————————————————————————————————
这个应该还有更好的证明方式或者思维方法,明天再补:
—————————————————————————————————————————————————————
D题:Minimum path
题解写在了代码里面
1 /* 2 这道题最重要的是分层思想 3 这个的话我们需要改动一点点东西才能把这个变化好 4 5 6 首先我们可以清楚的认识到一点这个串的长度为2*n-1 7 然后我们的k都应该是要变化在前面。。。。。。。。 8 这个是应该要注意到的一点 9 10 然后我们应该用分层思想来做这道题,对i+j-1相同的 11 点,他们肯定都是s[i+j-1]的备选项 12 13 然后我们怎么确定这个能一直被a延续到呢? 14 用个小dp处理一下就ok了,dp[i][j]表示到[i][j]的 15 路上的a的数量。 16 我们只要保证dp[i][j]+k>=i+j-1;就可以知道这一 17 纬度是可行的了。然后我们这样如果取出多个点。由于 18 是分层的,这样的数量很少,我们接下来就直接bfs即可 19 这样还是不难想的,每层取最小值即可 20 21 —————————————————————————————————————————————— 22 这个题的关键突破点在于我们得处理出这个串是定长的 23 然后他的每一步的贡献都是从i+j-1的s[i][j]中选出来的 24 25 明白这个关键后,我们显然的想法是让能延续的更长,那么我们 26 只要找到最大的i+j-1满足dp[i][j]+k>=i+j-1即可,这个用个小 27 dp处理就ok 28 29 找到以后还是由于分段思想,直接bfs就可以了 30 时间复杂度是o(n^2)的 31 —————————————————————————————————————————————— 32 思维路线: 33 性质(1):串是定长,定长是2*n-1 34 性质(2):我们知道对于i+j-1一样的s[i][j];都有可能成为 35 这个串第i+j-1的值的选项。 36 37 性质1应该是很容易想的,性质2的话如果要围绕着确定我们单独 38 这个字符串的每一位来想的话也很容易 39 40 那么很容易得到一个贪心的想法 41 我要让a衍生的尽量长,那么我们就需要把它能放上a尽量发a, 42 那么我们根据性质2可以处理出一个小dp,这个小dp的话就很 43 好搞了,取得dp[i][j]+k>=(i+j-1)的最大值的i+j-1 44 这个就是最终的a的衍生长度 45 46 后面也很好想,就是多起点的bfs 47 ———————————————————————————————————————————— 48 主要是这两个性质,其他是设计算法来套这个性质 49 ————————————————————————————————————————————— 50 */ 51 #include <cstdio> 52 #include <cstring> 53 #include <cmath> 54 #include <bitset> 55 #include <algorithm> 56 #include <iostream> 57 #include <vector> 58 typedef long long ll; 59 using namespace std; 60 const int maxn=(int)(2e3+1000); 61 int n,k; 62 char s[maxn][maxn]; 63 vector<int> vec[maxn*2]; 64 int ai[maxn][maxn]; 65 int vis[maxn*2][maxn*2]; 66 char q[maxn*3]; 67 char l[maxn]; 68 69 70 int main(){ 71 scanf("%d%d",&n,&k); 72 for(int i=1;i<=n;i++) scanf("%s",s[i]+1); 73 if(k>=(2*n)-1){ 74 for(int i=1;i<=(2*n)-1;i++) printf("a"); printf("\n"); return 0; 75 } 76 // 对每个位置到达的顺序为 i+j-1; 77 // 积累到这一步的a的数量 如果a[i][j]+k>=i+j-1; 78 // 那么这就是一个可行点,然后找出所有的可行点 79 for(int i=1;i<=n;i++){ 80 for(int j=1;j<=n;j++){ 81 ai[i][j]=max(ai[i-1][j],ai[i][j-1]); 82 if(s[i][j]=='a') ai[i][j]++; 83 } 84 } 85 int ans=0; 86 for(int i=1;i<=n;i++){ 87 for(int j=1;j<=n;j++){ 88 if(ai[i][j]+k>=i+j-1) ans=max(ans,i+j-1); 89 } 90 } 91 if(ans==(2*n)-1){ 92 for(int i=1;i<=(2*n)-1;i++) printf("a"); printf("\n");return 0; 93 } 94 for(int i=1;i<=n;i++){ 95 for(int j=1;j<=n;j++){ 96 if(ai[i][j]+k>=i+j-1&&ans==i+j-1) vis[i][j]=1; 97 } 98 } 99 int sss=0; 100 if(ans==0){ 101 q[ans+1]=s[1][1]; 102 ans=1;sss=1; 103 vis[1][1]=1; 104 } 105 106 for(int l=ans;l<=(2*n)-2;l++){ 107 int cnt=0,mn=28; 108 for(int i=1;i<=n;i++){ 109 int j=l-i+1; 110 if(j<=0||j>n) continue; 111 if(vis[i][j]!=1) continue; 112 int q1=-1,q2=-1; 113 if(i+1<=n) q1=s[i+1][j]-'a'; 114 if(j+1<=n) q2=s[i][j+1]-'a'; 115 if(q1!=-1) mn=min(q1,mn); 116 if(q2!=-1) mn=min(q2,mn); 117 }q[l+1]=(char)(mn+'a'); 118 for(int i=1;i<=n;i++){ 119 int j=l-i+1; 120 if(j<=0||j>n) continue; 121 if(vis[i][j]!=1) continue; 122 int q1=-1,q2=-1; 123 if(i+1<=n) q1=s[i+1][j]-'a'; 124 if(j+1<=n) q2=s[i][j+1]-'a'; 125 if(q1==mn){vis[i+1][j]=1;} 126 if(q2==mn){vis[i][j+1]=1;} 127 } 128 } 129 130 if(sss==1){ 131 printf("%c",q[1]); 132 }else{ 133 for(int i=1;i<=ans;i++) printf("a"); 134 } 135 for(int i=ans+1;i<=(2*n)-1;i++) printf("%c",q[i]); 136 printf("\n"); 137 return 0; 138 }