Codeforces Round #545题解
A题
做法很明显,就是隔壁连续1和连续2取min,虽然写的比较复杂
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10; int a[N]; queue<int> q1,q2; int main(){ int n; cin>>n; int i; int ans=0; for(i=1;i<=n;i++){ cin>>a[i]; } int cnt1=0,cnt2=0; int last=0; int flag=0; for(i=2;i<=n;i++){ if(a[i-1]!=a[i]){ if(!flag){ flag=a[i-1]; } if(a[i-1]==1){ q1.push(i-1-last); } else{ q2.push(i-1-last); } last=i-1; } } if(a[n]==2){ q2.push(n-last); } else{ q1.push(n-last); } while(q1.size()&&q2.size()){ int t1=q1.front(); int t2=q2.front(); ans=max(ans,min(t1,t2)); if(flag==1){ q1.pop(); flag=2; } else{ flag=1; q2.pop(); } } cout<<ans*2<<endl; return 0; }
B题
这题如果直接模拟感觉讨论的情况会比较多,因此我们列方程式来做,这也会列出两个等式,我们枚举只会a的个数和每边存在会的人数的总数,这也所有变量都能够被求出来
只有去掉非法情况并判断结果即可,没有则输出-1
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10; int a[N],c[N],n; vector<int> num[5]; int main(){ cin>>n; int i,j,k; int cnt1=0,cnt2=0,cnt3=0,cnt4=0; string s; cin>>s; s=" "+s; for(i=1;i<(int)s.size();i++){ c[i]=(s[i]=='1')?1:0; } cin>>s; s=" "+s; for(i=1;i<(int)s.size();i++){ a[i]=(s[i]=='1')?1:0; } for(i=1;i<=n;i++){ if(c[i]&&!a[i]){ cnt1++; num[1].push_back(i); } if(!c[i]&&a[i]){ cnt2++; num[2].push_back(i); } if(c[i]&&a[i]){ cnt3++; num[3].push_back(i); } if(!c[i]&&!a[i]){ num[4].push_back(i); cnt4++; } } for(i=0;i<=cnt1+cnt3;i++){ for(j=0;j<=i&&j<=cnt1;j++){ int tmp=i-j; if(tmp<0||tmp>cnt3) continue; int b=cnt2-2*i+cnt3+j; if(b<0||b>cnt2) continue; int d1=n/2-b-i; int d2=n/2-cnt1-cnt2-cnt3+b+i; if(d1<0||d1>cnt4) continue; if(d2<0||d2>cnt4) continue; if(d1+d2==cnt4){ for(int k=0;k<j;k++){ cout<<num[1][k]<<" "; } for(int k=0;k<b;k++){ cout<<num[2][k]<<" "; } for(int k=0;k<i-j;k++){ cout<<num[3][k]<<" "; } for(int k=0;k<d1;k++){ cout<<num[4][k]<<" "; } cout<<endl; return 0; } } } cout<<-1<<endl; return 0; }
C题
这题比B题简单,重新赋值其实相当于找离散化的值,我们又注意到,只有交点对左右两边都有影响,因此取这个交点在行列中离散话较大的值,因为这个交点只能取一个值
显然小的就要变成大的,以此类推,全部平移差值之后取行列max
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=2e6+10; int a[1010][1010]; vector<int> row[N]; vector<int> col[N]; int ans[1010][1010]; int main(){ ios::sync_with_stdio(false); int n,m; cin>>n>>m; int i,j; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ cin>>a[i][j]; row[i].push_back(a[i][j]); col[j].push_back(a[i][j]); } } for(i=1;i<=n;i++){ sort(row[i].begin(),row[i].end()); row[i].erase(unique(row[i].begin(),row[i].end()),row[i].end()); } for(i=1;i<=m;i++){ sort(col[i].begin(),col[i].end()); col[i].erase(unique(col[i].begin(),col[i].end()),col[i].end()); } for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ int pos1=lower_bound(row[i].begin(),row[i].end(),a[i][j])-row[i].begin()+1; int pos2=lower_bound(col[j].begin(),col[j].end(),a[i][j])-col[j].begin()+1; int mx; if(pos1>pos2){ mx=(int)col[j].size()+pos1-pos2; ans[i][j]=max(mx,(int)row[i].size()); } else{ mx=(int)row[i].size()+pos2-pos1; ans[i][j]=max(mx,(int)col[j].size()); } } } for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ cout<<ans[i][j]<<" "; } cout<<endl; } }
D题
贪心思路,因为要求出现次数最多,所以除去非法情况,我们刚开始直接生成一个t一定不劣
那么之后我们发现,只要找到t串前后缀最大的地方,这样是最优的,因为可以用最小的代价又产生一个t串,那么这个其实就是kmp算法。
之后就是不停循环构造直到字符不够
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=2e6+10; int ne[N]; int main(){ ios::sync_with_stdio(false); string s,t; cin>>s>>t; int i,j; int cnt1=0,cnt0=0; if(t.size()>s.size()){ cout<<s<<endl; return 0; } s=" "+s; t=" "+t; string res=" "; for(i=1;i<(int)s.size();i++){ if(s[i]=='1') cnt1++; else cnt0++; } int flag=0; for(i=1;i<(int)t.size();i++){ if(t[i]=='1'){ if(cnt1){ res+=t[i]; cnt1--; } else{ flag=1; } } else{ if(cnt0){ res+=t[i]; cnt0--; } else{ flag=1; } } } if(flag){ cout<<s.substr(1)<<endl; return 0; } for(i=2,j=0;i<(int)t.size();i++){ while(j&&t[i]!=t[j+1]){ j=ne[j]; } if(t[j+1]==t[i]){ j++; } ne[i]=j; } int pos=ne[(int)t.size()-1]+1; flag=0; while(1){ for(i=pos;i<(int)t.size();i++){ if(t[i]=='1'){ if(cnt1){ res+=t[i]; cnt1--; } else{ flag=1; break; } } else{ if(cnt0){ res+=t[i]; cnt0--; } else{ flag=1; break; } } } if(flag) break; pos=ne[(int)t.size()-1]+1; } while(cnt1--){ res+='1'; } while(cnt0--){ res+='0'; } cout<<res.substr(1); }
E题
本题思路比较巧妙,首先要观察的一点是,每个点都能被看成d个点,表示每个天数,这是根据我们的题目发现天数很少可以看出来
那么接下来考虑如何转移,这时候我们发现因为有环,所以不能直接跑,一般环问题我们都会转化成缩点变成dag问题,这题也不例外,只要缩完点,那么一个连通分量里能走到的点的权值肯定是已知的
之后就是去跑最长路即可,用这种办法写很卡空间,很不幸,我没卡过去,这里会MLE在73个点,建议观看别人的代码卡空间,有空我再调一调
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=100005*50; int n,m,d; int h[N],ne[N],e[N],idx; int dfn[N],low[N],times; stack<int> q,tmp; ll dis[200100]; int scnt,id[N]; int vis[N],ins[N]; int f[N]; int val[N]; int h1[N],ne1[N],e1[N],idx1; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void add1(int a,int b){ e1[idx1]=b,ne1[idx1]=h1[a],h1[a]=idx1++; } int get(int i,int j){ return j*n+i; } void tarjan(int u){ dfn[u]=low[u]=++times; q.push(u); ins[u]=1; int i; for(i=h[u];i!=-1;i=ne[i]){ int j=e[i]; if(!dfn[j]){ tarjan(j); low[u]=min(low[u],low[j]); } else if(ins[j]){ low[u]=min(low[u],dfn[j]); } } if(dfn[u]==low[u]){ ++scnt; while(1){ int t=q.top(); q.pop(),ins[t]=0; id[t]=scnt; int x,y; x=(t-1)%n+1,y=(t-1)/n; if(!vis[x]&&((dis[x]>>y)&1)){ val[scnt]++; vis[x]=1; tmp.push(x); } if(t==u) break; } } while(tmp.size()){ auto t=tmp.top(); tmp.pop(); vis[t]=0; } } int dfs(int u){ if(vis[u])return f[u]; vis[u]=1; for(int i=h1[u];i!=-1;i=ne1[i]){ int v=e1[i]; f[u]=max(f[u],dfs(v)); } return f[u]+=val[u]; } int main(){ ios::sync_with_stdio(false); cin>>n>>m>>d; int i,j; memset(h,-1,sizeof h); memset(h1,-1,sizeof h1); for(i=1;i<=m;i++){ int u,v; cin>>u>>v; for(j=0;j<=d-1;j++){ add(get(u,j),get(v,(j+1)%d)); } } for(i=1;i<=n;i++){ string s; cin>>s; for(j=0;j<d;j++){ dis[i]|=1ll*(s[j]-'0')<<j; } } tarjan(1); for(i=1;i<=n*d;i++){ if(!dfn[i])continue; int u=id[i]; for(int j=h[i];j!=-1;j=ne[j]){ int v=e[j]; if(!dfn[v]) continue; v=id[v]; if(u!=v){ add1(u,v); } } } cout<<dfs(id[1])<<endl; }
F题
交互题,使用floyd消圈法,因为不太想写交互题,所以这题就略过了
没有人不辛苦,只有人不喊疼