Codeforces Round #599 (Div. 2)
A. Maximum Square
直接O(kn^2)枚举最大边长
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int k,n,a[1005]; int main(){ cin>>k; while(k--){ cin>>n; for(int i=1;i<=n;++i) cin>>a[i]; for(int i=n;i>=1;--i){ int sum=0; for(int j=1;j<=n;++j) if(a[j]>=i) ++sum; if(sum>=i){printf("%d\n",i);break;} } }return 0; }
B1. Character Swap (Easy Version)
合法当且仅当两串恰有2对相同的不同字符
(即$a[i]==a[j],b[i]==b[j],a[i]!=b[i]$)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int T,n,p[256][256]; char a[10005],b[10005]; int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); scanf("%s",a+1); scanf("%s",b+1); memset(p,0,sizeof(p)); for(int i=1;i<=n;++i) if(a[i]!=b[i]) ++p[a[i]][b[i]]; int sum=0; for(int i='a';i<='z';++i) for(int j='a';j<='z';++j){ if(p[i][j]==2) ++sum; if(p[i][j]==1||p[i][j]>2) sum=5; } puts(sum<=1?"Yes":"No"); }return 0; }
B2. Character Swap (Hard Version)
$<=2n$次交换意味着可以直接模拟
不合法的情况当且仅当有字符在两串中出现次数为奇数
设当前到第$i$位,$a[i]!=b[i]$
直接枚举找与$a[i]$相同的字符$a[k]$或$b[k]$
如果为$a[k]$,$swap(b[i],a[k])$
如果为$b[k]$,选择任意一个下标$j$,$swap(a[j],b[k]),swap(a[j],b[i])$
这样每位最多交换2次
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int T,n,m,p1[256],r1[200],r2[200]; char a[53],b[53]; int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); scanf("%s",a+1); scanf("%s",b+1); memset(p1,0,sizeof(p1)); for(int i=1;i<=n;++i) ++p1[a[i]],++p1[b[i]]; bool ok=1; for(int i='a';i<='z'&&ok;++i) if(p1[i]&1) ok=0; if(!ok){puts("No"); continue;} puts("Yes"); m=0; for(int i=1;i<=n;++i) if(a[i]!=b[i]){ for(int j=i+1;j<=n;++j){ if(a[j]==a[i]){ r1[++m]=j; r2[m]=i; swap(a[j],b[i]); break; } if(b[j]==a[i]){ r1[++m]=i+1; r2[m]=j; swap(a[i+1],b[j]); r1[++m]=i+1; r2[m]=i; swap(a[i+1],b[i]); break; } } } printf("%d\n",m); for(int i=1;i<=m;++i) printf("%d %d\n",r1[i],r2[i]); }return 0; }
C. Tile Painting
将$n$个点每隔$p-1(p>1,p|n)$个连边,求连通块个数
可以发现存在$p_1,p_2$时,连通块个数=$gcd(p_1,p_2)$
于是$O(\sqrt n)$枚举$n$的约数判断一下就好了
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; typedef long long ll; ll n,m,p[300],tt,len; int main(){ cin>>n; m=len=n; for(ll i=2,t=sqrt(n);i<=t;++i){ if(m<i) break; if(m%i==0) p[++tt]=i; while(m%i==0) m/=i; } if(m>1) p[++tt]=m; for(int i=tt;i>=1;--i){ if(len%p[i]){len=1; break;} len=p[i]; }cout<<len; return 0; }
D. 0-1 MST
可以把0边用并查集维护,最后求连通块个数
但是$0$边过多
于是大力套个线段树优化建图就完事了
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; #define N 100005 int n,m,fa[N*5],id[N]; bool vis[N*5],ve[N*5]; int rt,u,s[N*5],lc[N*5],rc[N*5]; vector <int> g[N]; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void uni(int x,int y){ int r1=find(x),r2=find(y); if(r1!=r2) fa[r1]=r2; } #define mid (l+r)/2 int build(int l,int r){ int nw=++u; fa[nw]=nw; if(l==r){id[l]=nw; return nw;} lc[nw]=build(l,mid); rc[nw]=build(mid+1,r); return nw; } void link(int o,int l,int r,int x1,int x2,int k){ if(x1<=l&&r<=x2){vis[o]=1; uni(o,k); return ;} if(x1<=mid) link(lc[o],l,mid,x1,x2,k); if(x2>mid) link(rc[o],mid+1,r,x1,x2,k); } void tree_uni(int o,int l,int r){ if(vis[o]) for(int i=l;i<=r;++i) uni(o,id[i]); if(l==r) return ; tree_uni(lc[o],l,mid); tree_uni(rc[o],mid+1,r); } void Link(int I,int l,int r){ link(rt,1,n,l,r,id[I]); } int main(){ scanf("%d%d",&n,&m); for(int i=1,U,V;i<=m;++i){ scanf("%d%d",&U,&V); g[U].push_back(V); g[V].push_back(U); } rt=build(1,n); for(int i=1;i<=n;++i){ g[i].push_back(i); sort(g[i].begin(),g[i].end()); int len=g[i].size(); for(int j=0;j<len-1;++j){ int l=g[i][j],r=g[i][j+1]; if(l+1<r) Link(i,l+1,r-1); } if(1<g[i][0]) Link(i,1,g[i][0]-1); if(g[i][len-1]<n) Link(i,g[i][len-1]+1,n); } tree_uni(rt,1,n); int tot=0; for(int i=1;i<=n;++i){ int Fa=find(id[i]); if(!ve[Fa]) ve[Fa]=1,++tot; } printf("%d",tot-1); return 0; }
E. Sum Balance
WA#9......不管了先睡了(错乱)