省选测试7

总结

凭着第三题一个剪枝到了第二

\(T1\) 基环树基本都能想到,大部分人都是实现出了问题

\(T2\) \(8 \times n^2\) 能够 \(2e4\) 挺玄学的

\(T3\) 加个剪枝 \(n^22^n\) 就过了

A. 同桌的你

分析

因为每一个点只有一个出度所以最终一定会形成一个基环森林

然后每一次找出环,把环断掉跑一个最大匹配就行了

一种写法是只断一条边然后然后强制这条边选或者不选,还有一种写法是分别断一个环上的两条边后选一个最大的

因为要在匹配数尽量大的前提下让异性配对的人尽量多

所以可以把同性之间的边权设为 \(1e6\),异性之间的边权设为 \(1e6+1\)

第一问答案就是最大匹配除以 \(1e6\),第二问的答案就是最大匹配对 \(1e6\) 取模

输出方案的时候找一下最优决策点就行了

代码

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cmath>
#include<set>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e6+5;
int t,n,a[maxn],x[maxn],h[maxn],tot=2,sta[maxn],jl1,jl2,tp;
#define Max(a,b) (a>b?a:b)
struct asd{
	int to,nxt,val;
}b[maxn<<1];
void ad(rg int aa,rg int bb,rg int cc){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	b[tot].val=cc;
	h[aa]=tot++;
}
bool vis[maxn],ishuan,cut[maxn<<1];
void dfs(rg int now,rg int lat,rg int pre){
	vis[now]=1;
	sta[++tp]=now;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		if(vis[u]){
			if(!ishuan) jl1=i,jl2=pre,ishuan=1;
		} else {
			dfs(u,now,i);
		}
	}
}
long long f[maxn][2],ans,g[maxn][2];
void dfs2(rg int now,rg int lat){
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || cut[i]) continue;
		dfs2(u,now);
		f[now][0]+=Max(f[u][1],f[u][0]);
	}
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || cut[i]) continue;
		f[now][1]=Max(f[now][1],f[now][0]-Max(f[u][0],f[u][1])+f[u][0]+b[i].val);
	}
}
void pri(rg int now,rg int lat,rg int op){
	if(op==0){
		for(rg int i=h[now];i!=-1;i=b[i].nxt){
			rg int u=b[i].to;
			if(cut[i] || u==lat) continue;
			pri(u,now,f[u][1]>f[u][0]);
		}
	} else {
		rg int jud=0;
		for(rg int i=h[now];i!=-1;i=b[i].nxt){
			rg int u=b[i].to;
			if(u==lat || cut[i]) continue;
			if(!jud && f[now][1]==f[now][0]-Max(f[u][0],f[u][1])+f[u][0]+b[i].val){
				printf("%d %d\n",now,u);
				jud=1;
				pri(u,now,0);
			} else {
				pri(u,now,f[u][1]>f[u][0]);
			}
		}
	}
}
int judrt[maxn];
int main(){
	t=read();
	while(t--){
		memset(h,-1,sizeof(h));
		memset(f,0,sizeof(f));
		memset(vis,0,sizeof(vis));
		memset(cut,0,sizeof(cut));
		ans=0,tot=2;
		n=read();
		for(rg int i=1;i<=n;i++){
			vis[i]=0;
			a[i]=read(),x[i]=read();
		}
		for(rg int i=1;i<=n;i++){
			if(a[a[i]]!=i){
				if(x[i]==x[a[i]]){
					ad(i,a[i],1e6);
					ad(a[i],i,1e6);
				} else {
					ad(i,a[i],1e6+1);
					ad(a[i],i,1e6+1);
				}
			} else {
				if(i<a[i]){
					if(x[i]==x[a[i]]){
						ad(i,a[i],1e6);
						ad(a[i],i,1e6);
					} else {
						ad(i,a[i],1e6+1);
						ad(a[i],i,1e6+1);
					}
				}
			}
		}
		rg long long tmp=0;
		for(rg int i=1;i<=n;i++){
			if(!vis[i]){
				ishuan=0,tp=0;
				dfs(i,0,0);
				if(ishuan){
					tmp=0;
					cut[jl1]=cut[jl1^1]=1;
					dfs2(b[jl1].to,0);
					tmp=Max(tmp,Max(f[b[jl1].to][0],f[b[jl1].to][1]));
					cut[jl1]=cut[jl1^1]=0;
					for(rg int j=1;j<=tp;j++){
						g[sta[j]][0]=f[sta[j]][0],g[sta[j]][1]=f[sta[j]][1];
						f[sta[j]][0]=f[sta[j]][1]=0;
					}
					cut[jl2]=cut[jl2^1]=1;
					dfs2(b[jl2].to,0);
					cut[jl2]=cut[jl2^1]=0;
					if(tmp<Max(f[b[jl2].to][0],f[b[jl2].to][1])){
						tmp=Max(f[b[jl2].to][0],f[b[jl2].to][1]);
						judrt[b[jl2].to]=1;
						cut[jl2]=cut[jl2^1]=1;
					} else {
						for(rg int j=1;j<=tp;j++){
							f[sta[j]][0]=g[sta[j]][0],f[sta[j]][1]=g[sta[j]][1];
						}
						judrt[b[jl1].to]=1;
						cut[jl1]=cut[jl1^1]=1;
					}
					ans+=tmp;
				} else {
					dfs2(i,0);
					judrt[i]=1;
					ans+=Max(f[i][0],f[i][1]);
				}
			}
		}
		long long ans1=ans/1000000,ans2=ans%1000000;
		printf("%lld %lld\n",ans1,ans2);
		for(rg int i=1;i<=n;i++){
			if(judrt[i]){
				pri(i,0,f[i][1]>f[i][0]);
				judrt[i]=0;
			}
		}
	}
	return 0;
}

B. 大水题

分析

因为奶牛的种类数不会很多,所以可以枚举选择的奶牛种类的状态

强制这个区间内只能选这些种类的奶牛

然后把差分后的状态哈希一下存进哈希表里

比如说当前奶牛的个数分别为 \(1\ 2\ 2\)

那么我们就可以把这个状态看成 \(0\ 1\ 1\)

去哈希表里找一下之前有没有这样的状态就行了

如果当前出现了不合法的奶牛种类,直接把哈希表清空即可

代码

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<cstring>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e6+5,maxk=15,mod=1e5+3;
typedef unsigned long long ull;
const ull bas=2333333;
int n,k,ans=-1,sum[maxn][maxk],cnt[maxk],totcnt,mmax,sta2[maxn],tp2;
struct jie{
	int wz,num;
}c[maxn];
bool cmp(rg jie aa,rg jie bb){
	return aa.wz<bb.wz;
}
struct has{
	int h[maxn],tot,sta[maxn],tp;
	struct asd{
		int nxt,beg;
		ull val;
	}b[maxn];
	void init(){
		memset(h,-1,sizeof(h));
		tot=1;
	}
	void clear(){
		for(rg int i=1;i<=tp;i++) h[sta[i]]=-1;
		tot=1,tp=0;
	}
	void ad(rg ull val,rg int num){
		rg int now=val%mod;
		for(rg int i=h[now];i!=-1;i=b[i].nxt){
			if(b[i].val==val) return;
		}
		b[tot].nxt=h[now],b[tot].val=val,b[tot].beg=num,h[now]=tot++;
		sta[++tp]=now;
	}
	int cx(rg ull val){
		rg int now=val%mod;
		for(rg int i=h[now];i!=-1;i=b[i].nxt){
			if(b[i].val==val) return b[i].beg;
		}
		return -1;
	}
}mp;
int main(){
	n=read(),k=read();
	for(rg int i=1;i<=n;i++){
		c[i].wz=read(),c[i].num=read();
		totcnt=std::max(totcnt,c[i].num);
	}
	std::sort(c+1,c+n+1,cmp);
	for(rg int i=1;i<=n;i++){
		for(rg int j=1;j<=totcnt;j++) sum[i][j]=sum[i-1][j];
		sum[i][c[i].num]++;
	}
	mmax=(1<<totcnt)-1;
	rg int ncnt=0;
	for(rg int i=1;i<=mmax;i++){
		ncnt=0;
		for(rg int j=1;j<=totcnt;j++){
			if(i&(1<<(j-1))) ncnt++;
		}
		if(ncnt>=k) sta2[++tp2]=i;
	}
	rg int zt=0,now=0,lat;
	rg ull nhas=0;
	mp.init();
	for(rg int i=1;i<=tp2;i++){
		zt=sta2[i];
		mp.clear();
		lat=0;
		for(rg int j=1;j<=totcnt;j++) cnt[j]=0;
		for(rg int j=1;j<=n;j++){
			if(zt&(1<<(c[j].num-1))){
				cnt[c[j].num]++;
				rg int mmax=0x3f3f3f3f;
				for(rg int k=1;k<=totcnt;k++){
					if(zt&(1<<(k-1))) mmax=std::min(mmax,cnt[k]);
				}
				for(rg int k=1;k<=totcnt;k++){
					if(zt&(1<<(k-1))) cnt[k]-=mmax;
				}
				nhas=0;
				for(rg int k=1;k<=totcnt;k++){
					nhas=nhas*bas+(ull)k*cnt[k];
				}
				if(nhas==0){
					ans=std::max(ans,c[j].wz-c[lat+1].wz);
				}
				now=mp.cx(nhas);
				if(now!=-1){
					ans=std::max(ans,c[j].wz-c[now+1].wz);
				} else {
					mp.ad(nhas,j);
				}
			} else {
				mp.clear();
				for(rg int k=1;k<=totcnt;k++) cnt[k]=0;
				lat=j,nhas=0;
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}

C. 佛罗里达

分析

把人抽象成节点,那么人和人之间的边权就是它们的矛盾值

这种两个限制的问题肯定要把限制变成一个

所以我们把边权从大到小排序

枚举每一条边,强制左边的集合必须选这条边并且这条边的价值是最大的

对于所有边权比这条边小的边,我们最后肯定可以都把它们放到左边的集合中

所以需要考虑的只是边权比这条边大的边如何放

我们把所有边权比这条边大的边拉出来进行奇偶染色

如果出现了奇环肯定无解,而且之后也一定无解,直接 \(break\) 就行了

否则我们肯定要把这些点分成两部分,一部分给左边的集合,一部分给右边的集合

如果两个点在同一个联通块中并且染色相同,那么就必须划分到一个集合中

对于和当前边的左右端点在一个联通块中并且染色相同的点,强制放到左边的集合中

而且要满足右边的集合价值最小

这个问题我也没有什么好的做法,直接跑一个 \(dfs\) 就行了

复杂度肯定是不对的,但是跑得比正解还快,貌似也卡不住

代码

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=255;
int n,a[maxn][maxn],ans=0x7f7f7f7f,tot,nans;
struct asd{
	int zb,yb,val;
	asd(){}
	asd(rg int aa,rg int bb,rg int cc){
		zb=aa,yb=bb,val=cc;
	}
}b[maxn*maxn];
bool cmp(rg asd aa,rg asd bb){
	return aa.val>bb.val;
}
std::vector<int> g[maxn],tmp1[maxn],tmp2[maxn];
int cnt,shuyu[maxn],col[maxn],sta[maxn],tp,nid;
bool vis[maxn],haha,jud[maxn];
void dfs(rg int now,rg int ncol){
	col[now]=ncol;
	shuyu[now]=cnt;
	for(rg int i=0;i<g[now].size();i++){
		if(!col[g[now][i]]){
			dfs(g[now][i],3-ncol);
		} else {
			if(col[now]==col[g[now][i]]) haha=1;
		}
	}
}
void dfs2(rg int now,rg int mans){
	if(mans>=nans) return;
	if(mans+b[nid].val>=ans) return;
	if(now>cnt){
		nans=std::min(nans,mans);
		return;
	}
	rg int tmp=mans;
	for(rg int i=0;i<tmp1[now].size();i++){
		sta[++tp]=tmp1[now][i];
		for(rg int j=1;j<tp;j++){
			tmp=std::max(tmp,std::max(a[sta[tp]][sta[j]],a[sta[j]][sta[tp]]));
		}
	}
	dfs2(now+1,tmp);
	for(rg int i=0;i<tmp1[now].size();i++) tp--;
	if(!jud[now]){
		tmp=mans;
		for(rg int i=0;i<tmp2[now].size();i++){
			sta[++tp]=tmp2[now][i];
			for(rg int j=1;j<tp;j++){
				tmp=std::max(tmp,std::max(a[sta[tp]][sta[j]],a[sta[j]][sta[tp]]));
			}
		}
		dfs2(now+1,tmp);
		for(rg int i=0;i<tmp2[now].size();i++) tp--;
	}
}
int main(){
	while(scanf("%d",&n)!=EOF){
		ans=0x7f7f7f7f,haha=0,tot=0;
		for(rg int i=1;i<=n;i++){
			for(rg int j=i+1;j<=n;j++){
				a[i][j]=read();
				b[++tot]=asd(i,j,a[i][j]);
			}
		}
		for(rg int i=1;i<=n;i++) vis[i]=0,g[i].clear();
		if(n==2){
			printf("0\n");
			continue;
		}
		std::sort(b+1,b+tot+1,cmp);
		rg int aa,bb;
		for(rg int i=1;i<=tot;i++){
			for(rg int j=1;j<=n;j++) jud[j]=0,shuyu[j]=0,col[j]=0,tmp1[j].clear(),tmp2[j].clear();
			cnt=0,nid=i;
			if(b[i].val<b[i-1].val){
				for(rg int j=i-1;j>=1;j--){
					if(b[j].val!=b[i-1].val) break;
					aa=b[j].zb,bb=b[j].yb;
					g[aa].push_back(bb),g[bb].push_back(aa);
					vis[aa]=vis[bb]=1;
				}
			}
			for(rg int j=1;j<=n;j++){
				if(vis[j] && !shuyu[j]){
					cnt++;
					dfs(j,2);
				}
			}
			if(haha) break;
			if(shuyu[b[i].zb]==shuyu[b[i].yb] && col[b[i].zb]!=col[b[i].yb]) continue;
			for(rg int j=1;j<=n;j++){
				if(vis[j]){
					if(shuyu[b[i].zb]==shuyu[j]){
						if(col[b[i].zb]!=col[j]){
							tmp1[shuyu[j]].push_back(j);
							jud[shuyu[j]]=1;
						}
					} else if(shuyu[b[i].yb]==shuyu[j]){
						if(col[b[i].yb]!=col[j]){
							tmp1[shuyu[j]].push_back(j);
							jud[shuyu[j]]=1;
						}
					} else {
						if(col[j]&1) tmp1[shuyu[j]].push_back(j);
						else tmp2[shuyu[j]].push_back(j);
					}
				}
			}
			nans=0x3f3f3f3f,tp=0;
			dfs2(1,0);
			ans=std::min(ans,nans+b[i].val);
		}
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2021-01-26 09:06  liuchanglc  阅读(82)  评论(0编辑  收藏  举报