Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2)
A.给定n*m的矩阵a,构造一个同样大小的矩阵b使得[1,n*m]都出现一次,且b和a在任意位置上都不相等。
特判完无解后循环移位即可。
#include<bits/stdc++.h> using namespace std; int a[12][12]; void solve(){ int n,m; cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; if(n==1&&m==1){ cout<<-1<<"\n"; return; } if(n==1){ for(int i=1;i<m;i++) cout<<a[1][i+1]<<" "; cout<<a[1][1]<<"\n"; return; } if(m==1){ for(int i=1;i<n;i++) cout<<a[i+1][1]<<"\n"; cout<<a[1][1]<<"\n"; return; } for(int i=1;i<=n;i++){ for(int j=1;j<m;j++) cout<<a[i][j+1]<<" "; cout<<a[i][1]<<"\n"; } } int main(){ int t; cin>>t; while(t--){ //TODO solve(); } }
B.
#include<bits/stdc++.h> using namespace std; void solve(){ int n;cin>>n; string s,t;cin>>s>>t; int cnt=0; for(int i=0;i<n;i++) cnt+=(s[i]==t[i]); if(cnt==n){ cout<<"YES"<<"\n";return; } int prefix=-1; for(int i=0;i<n;i++){ if(s[i]=='0'&&t[i]=='0') prefix=i; else break; } if(s[1+prefix]=='0'&&t[1+prefix]=='1') { cout<<"NO"<<"\n"; } else cout<<"YES"<<"\n"; } int main(){ int t;cin>>t; while(t--){ //TODO solve(); } }
C.
倒着dp+小二分
#include<bits/stdc++.h> using namespace std; const int N = 2e5+5; int a[N]; typedef long long ll; ll sum[N],f[N]; void solve(){ int k;ll n; cin>>n>>k; sum[0]=0; for(int i=1;i<=n;i++){ cin>>a[i]; sum[i]=sum[i-1]+a[i]; } sum[n+1]=0; f[n+1]=0; for(int i=n;i>=1;i--){ if(a[i]>k){ f[i]=f[i+1]+1; } else { int ans=-1; int l=i+1,r=n; while(l<=r){ int mid=(l+r)>>1; if(sum[mid]-sum[i-1]>1ll*k){ ans=mid; r=mid-1; } else l=mid+1; } if(ans!=-1) { f[i]=f[ans+1]+1; } else f[i]=0; } } ll tot=n*(n-1)/2+n; for(int i=1;i<=n;i++) tot-=f[i]; cout<<tot<<"\n"; } int main(){ int t;cin>>t; while(t--){ //TODO solve(); } }
D.
考虑 |ai-aj| 能整除 x 其实就是在模x意义下同余,考虑到模数比较小,根据鸽笼原理肯定有两个点能连边。
又因为模数小的更容易满足,所以倒着从大开始做(从n-1开始),比较好处理的小模数放后面。
对于n-1必然能找到两个点连边,设为(u,v),接下来把u踢掉
问题转化成更小的子问题,从而一定有解。
#include<bits/stdc++.h> using namespace std; const int N = 2005; int a[N+5]; void solve(){ int n;cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; int vis[N+5]={0}; vector<pair<int,int>>ans; for(int i=n-1;i>=1;i--){ vector<int>mo[i+1]; for(int j=1;j<=n;j++){ if(vis[j]) continue; mo[a[j]%i].push_back(j); } int flag=0; for(int j=0;j<i && (flag==0) ;j++){ if(mo[j].size()>=2){ vis[mo[j][0]]=1; ans.push_back({mo[j][0],mo[j][1]}); flag=1; } } } cout<<"YES"<<"\n"; for(int i=ans.size()-1;i>=0;i--){ cout<<ans[i].first<<" "<<ans[i].second<<"\n"; } } int main(){ int t;cin>>t; while(t--){ solve(); } }
E
比D简单的诈骗题,发现大小为sz的树可以通过删叶子得到[1,sz]内的任意数,所以不考虑树的形态
然后贪心+分类讨论
设当前or出来的最大值为ans,当前考虑的数为x
从大到小拆位后,若当前位上x为0,跳过;否则若ans=0,x就可以对该位产生贡献,ans | = 1<< j ;若ans = 1,说明该位的贡献都已经得到了,且x>=(1<<j),就可以贪心地把x删成((1<<j)-1)使得后面的位都为1。
#include<bits/stdc++.h> using namespace std; const int N = 1e6+5; int a[N]; void solve(){ int k;cin>>k; for(int i=1;i<=k;i++){ cin>>a[i]; int x; for(int j=1;j<a[i];j++) cin>>x; } sort(a+1,a+k+1); int ans=0; for(int i=1;i<=k;i++){ for(int j=24;j>=0;j--){ int b1=(ans>>j)&1,b2=(a[i]>>j)&1; if(b2==0) continue; if(b1==0) ans|=(1<<j); else { ans|=((1<<j)-1); break; } } } cout<<ans<<"\n"; } int main(){ ios_base::sync_with_stdio(false); cin.tie(0);cout.tie(0); int t;cin>>t; while(t--){ solve(); } }