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;
}
posted @ 2022-01-15 23:59  Aireen_Ye  阅读(52)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.