cf Round #764(Div. 3)
C
二分图匹配裸题。
#include<bits/stdc++.h>
using namespace std;
const int N=55,M=255;
struct graph{
int nxt,to;
}e[M];
int a[N],g[N<<1],fr[N<<1],cnt,n,t;
bool u[N<<1];
inline void adde(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline bool match(int x){
for(int i=g[x];i;i=e[i].nxt)
if(!u[e[i].to]){
u[e[i].to]=true;
if(!fr[e[i].to]||match(fr[e[i].to])){
fr[e[i].to]=x;return true;
}
}
return false;
}
inline int hungary(){
int ret=0;
memset(fr,0,sizeof(fr));
for(int i=1;i<=n;i++){
memset(u,0,sizeof(u));
if(match(i)) ++ret;
}
return ret;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
cnt=0;
memset(g,0,sizeof(g));
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
while(a[i]){
if(a[i]<=n){
adde(i,a[i]+n);
}
a[i]/=2;
}
}
if(hungary()==n) printf("YES\n");
else printf("NO\n");
}
return 0;
}
D
Description
给定一个大小为n的字符串,把它们分给k个组(组不可以为空,但字符可以不分给任何组),组中字符要可以组成回文串,求最短字符串的最长长度。
Solution
解法一
二分+贪心。
贪心的时候,如果回文串长度为奇数,预先把最中间的字符取了(优先用总个数为奇数的字符,用总个数为偶数的字符时尽量集中)。
#include<bits/stdc++.h>
using namespace std;
const int N=200005,M=30;
int a[M],tot[M],n,k,t;
char s[N];
bool chk(int ans){
for(int i=0;i<26;++i) a[i]=tot[i];
if(ans&1){
--ans;
int cnt=0;
for(int i=0;i<26;++i)
if(a[i]&1){
--a[i];
if(++cnt==k) break;
}
for(int i=0,d;i<26&&cnt<k;++i){
d=min(a[i],k-cnt);
a[i]-=d;cnt+=d;
}
}
for(int i=1,len;i<=k;++i){
len=0;
for(int j=0,d;j<26;++j){
d=min(ans-len,a[j]);
if(d&1) --d;
len+=d;a[j]-=d;
}
if(len<ans) return false;
}
return true;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
scanf("%s",s+1);
memset(tot,0,sizeof(tot));
for(int i=1;i<=n;++i){
++tot[s[i]-'a'];
}
int l=0,r=n,mid;
while(l<r){
mid=(l+r+1)>>1;
if(chk(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
}
解法二
直接贪心,记录下有多少对偶数字符以及有多少个奇数字符,偶数字符平均分成k份后的剩余部分当奇数字符用,答案为偶数字符平均分成k份后的结果(+1)。(是否+1取决于奇数字符够不够用)
#include<bits/stdc++.h>
using namespace std;
const int N=200005,M=30;
int tot[M],n,k,t;
char s[N];
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
scanf("%s",s+1);
memset(tot,0,sizeof(tot));
for(int i=1;i<=n;++i){
++tot[s[i]-'a'];
}
int odd=0,ans=0;
for(int i=0;i<26;++i){
ans+=tot[i]/2;
odd+=tot[i]%2;
}
if(odd+ans%k*2>=k) printf("%d\n",ans/k*2+1);
else printf("%d\n",ans/k*2);
}
}
E
Description
给定n个长度为m的数字串\(s_i\),求数字串\(s'\)切割成若干长度\(\geq2\)的段 (每段在某个\(s_i\)中出现过) 的一种切割方案。
Solution
显然,长度>4的段都可由长度为2或3的段组成。
用桶记录所有长度为2或3的段出现的位置(每种只需要知道其中一个位置)。
正着做DP
f[i]表示前i个数字都已经匹配时,以i结尾的那个串的匹配位置。
若f[i-2]存在且s'[i-1...i]可匹配,f[i]=s'[i-1...i]匹配的位置。
若f[i-3]存在且s'[i-2...i]可匹配,f[i]=s'[i-2...i]匹配的位置。
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
struct range{
int l,r,i;
void init(){
l=r=i=0;
}
void set(int _l,int _r,int _i){
l=_l;r=_r;i=_i;
}
void print(){
printf("%d %d %d\n",l,r,i);
}
}f[N],f2[N],f3[N];
int nxt[N],n,m,t;
char s[N];
int num(char x,char y,char z){
return (x-'0')*100+(y-'0')*10+z-'0';
}
int main(){
scanf("%d",&t);
while(t--){
for(int i=0;i<N;++i){
f[i].init();f2[i].init();f3[i].init();
}
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) {
scanf("%s",s+1);
for (int j=1;j<m;++j) {
f2[num('0',s[j],s[j+1])].set(j,j+1,i);
if(j+1<m) f3[num(s[j],s[j+1],s[j+2])].set(j,j+2,i);
}
}
scanf("%s",s+1);
if(m==1){
printf("-1\n");
continue;
}
if(f2[num('0',s[1],s[2])].i){
f[2]=f2[num('0',s[1],s[2])];
}
if(m>=3&&f3[num(s[1],s[2],s[3])].i){
f[3]=f3[num(s[1],s[2],s[3])];
}
for(int i=4;i<=m;++i) {
if(f[i-2].i&&f2[num('0',s[i-1],s[i])].i)
f[i]=f2[num('0',s[i-1],s[i])];
else if(f[i-3].i&&f3[num(s[i-2],s[i-1],s[i])].i)
f[i]=f3[num(s[i-2],s[i-1],s[i])];
}
if(!f[m].i) printf("-1\n");
else{
int cnt=0;
for(int i=m;i;i-=(f[i].r-f[i].l+1)){
nxt[i-(f[i].r-f[i].l+1)]=i;
++cnt;
}
nxt[m]=m+1;
printf("%d\n",cnt);
for(int i=nxt[0];i<=m;i=nxt[i])
f[i].print();
}
}
return 0;
}
反着做DP
f[i]表示第i~m个数字都已经匹配时,以i开头的那个串的匹配位置。
若f[i+2]存在且s'[i...i+1]可匹配,f[i]=s'[i...i+1]匹配的位置。
若f[i+3]存在且s'[i...i+2]可匹配,f[i]=s'[i...i+2]匹配的位置。
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
struct range{
int l,r,i;
void init(){
l=r=i=0;
}
void set(int _l,int _r,int _i){
l=_l;r=_r;i=_i;
}
void print(){
printf("%d %d %d\n",l,r,i);
}
}f[N],f2[N],f3[N];
int nxt[N],n,m,t;
char s[N];
int num(char x,char y,char z){
return (x-'0')*100+(y-'0')*10+z-'0';
}
int main(){
scanf("%d",&t);
while(t--){
for(int i=0;i<N;++i){
f[i].init();f2[i].init();f3[i].init();
}
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) {
scanf("%s",s+1);
for (int j=1;j<m;++j) {
f2[num('0',s[j],s[j+1])].set(j,j+1,i);
if(j+1<m) f3[num(s[j],s[j+1],s[j+2])].set(j,j+2,i);
}
}
scanf("%s",s+1);
if(m>=2&&f2[num('0',s[m-1],s[m])].i){
f[m-1]=f2[num('0',s[m-1],s[m])];
}
if(m>=3&&f3[num(s[m-2],s[m-1],s[m])].i){
f[m-2]=f3[num(s[m-2],s[m-1],s[m])];
}
for(int i=m-3;i>0;--i) {
if(f[i+2].i&&f2[num('0',s[i],s[i+1])].i)
f[i]=f2[num('0',s[i],s[i+1])];
else if(f[i+3].i&&f3[num(s[i],s[i+1],s[i+2])].i)
f[i]=f3[num(s[i],s[i+1],s[i+2])];
}
if(!f[1].i) printf("-1\n");
else{
int cnt=0;
for(int i=1;i<=m;i+=(f[i].r-f[i].l+1)) ++cnt;
printf("%d\n",cnt);
for(int i=1;i<=m;i+=(f[i].r-f[i].l+1))
f[i].print();
}
}
return 0;
}
F
Description
交互题,给定n,需要猜出x \((1\leq x<n)\)。
你可以询问+c,则x=x+c,返回\(\lfloor\frac{x}{n}\rfloor\)
Solution
构造出二分即可:对于需要验证的mid,我们取能够使\(x_0=mid\)时,\(x_0+\sum c_i=0\;(mod\;n)\)的c。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,sc=0,ans;
scanf("%d",&n);
int l=1,r=n-1;
while(l<r){
int mid=(l+r+1)>>1;
int c=n-(sc+mid)%n;
sc+=c;
printf("+ %d\n",c);fflush(stdout);
scanf("%d",&ans);
l=max(l,ans*n-sc);
r=min(r,ans*n-sc+n-1);
}
printf("! %d\n",l+sc);fflush(stdout);
return 0;
}
G
Description
求权值为 \(w_1|w_2|...|w_{n-1}\) 的最小生成树。
Solution
从高位到低位贪心,如果当前位可以为0则丢弃所有当前位为1的边。只要图还能连通就可以丢。
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
struct edge{
int x,y,w;
}e[N];
int fa[N],n,m,t;
bool b[N];
int gf(int u){
if(u==fa[u]) return u;
return fa[u]=gf(fa[u]);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
}
for(int i=1;i<=m;++i) b[i]=true;
int ans=0;
for(int k=30;k>=0;--k){
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i)
if(b[i]&&!(e[i].w&(1<<k))){
if(gf(e[i].x)!=gf(e[i].y)) fa[gf(e[i].x)]=gf(e[i].y);
}
bool flag=true;
for(int i=1;i<=n;++i)
if(gf(i)!=gf(1)){
flag=false;break;
}
if(flag){
for(int i=1;i<=m;++i)
if(e[i].w&(1<<k)) b[i]=false;
}
else ans|=(1<<k);
}
printf("%d\n",ans);
}
return 0;
}