20260120 省选模拟赛

20260120 省选模拟赛

https://htoj.com.cn/cpp/oj/contest/detail?cid=22635323962240

Problem B. 白点黑点

对于度数序列,求出它能构造出的最大匹配有哪些。

最大匹配考虑 Hall 定理。对于一个集合 \(S\),其 \(|N(S)|\) 最小取 \(1\),最大取 \(\min(\sum_{x\in S} d_x,n)\)。从 \(1\) 开始,可以通过调整一条边使得 \(|N(S)|\) 增加 \(1\)这启发我们,最大匹配的取值可能是连续的。

结论:若构造出来的最小、最大的最大匹配分别为 \(l,r\),那么 \([l,r]\) 内的所有最大匹配都能构造出来。

\(l=r\) 显然成立。

\(l<r\),那么把最大匹配为 \(l\) 的连边方案拿出来,一定存在一个左部未匹配点 \(x\) 连向右部匹配点 \(u\),一个右部未匹配点 \(y\) 连向左部匹配点 \(v\),否则不可能有比 \(l\) 更大的最大匹配。断掉 \((x,u),(y,v)\),连接 \((u,v),(x,y)\),最大匹配增加 \(1\),而度数不变。

不难构造得出,设左部、右部度数非 \(0\) 点个数分别为 \(i,j\)那么 \(r=\min(i,j)\)

但是 \(l\) 不好求。放宽限制,我们钦定一个集合 \(S\),那么最大匹配一定 \(\le n-(|S|-|N(S)|)\)一定存在一个 \(S\) 让最大匹配达到 \(l\) 的下界

把钦定 \(S\)、确定度数的过程放到 dp 中解决。设 \(f(i,j,s,x,y)\) 为考虑前 \(i\) 个左部点,有 \(j\) 个度数非 \(0\) 点,\(|S|=s\)\(S\) 度数和为 \(x\),总度数和为 \(y\) 的最小代价,总复杂度 \(O(n^6)\)。右部点一样做,然后合并即可。

Note:Hall 定理使用不熟练。

int n,m;
int a[N][N],b[N][N];
ll f[N][N][N][N][N],g[N][N][N][N][N];
ll ans[N];

signed main(){
	read(n),read(m);
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++)
			read(a[i][j]);
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++)
			read(b[i][j]);
	}
	memset(ans,0x3f,sizeof(ans));
	memset(f,0x3f,sizeof(f));
	memset(g,0x3f,sizeof(g));
	f[0][0][0][0][0]=g[0][0][0][0][0]=0;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=i-1;j++)
			for(int s=0;s<=i-1;s++)
				for(int x=0;x<=m;x++)
					for(int y=x;y<=m;y++)
						for(int d=0;y+d<=m;d++){
							Ckmin(f[i][j+(d>0)][s+1][x+d][y+d],f[i-1][j][s][x][y]+a[i][d]);
							Ckmin(f[i][j+(d>0)][s][x][y+d],f[i-1][j][s][x][y]+a[i][d]);
							Ckmin(g[i][j+(d>0)][s+1][x+d][y+d],g[i-1][j][s][x][y]+b[i][d]);
							Ckmin(g[i][j+(d>0)][s][x][y+d],g[i-1][j][s][x][y]+b[i][d]);
						} 
	for(int i=0;i<=n;i++){
		for(int j=0;j<=n;j++){
			int R=min(i,j); 
			for(int s=0;s<=n;s++){
				ll mn=LINF;
				for(int t=0;t<=s;t++){
					int L=n-s+t;
					if(L>R) continue;
					for(int x=0;x<=m;x++){
						for(int y=x;y<=m;y++)
							Ckmin(mn,f[n][i][s][x][m]+g[n][j][t][y][m]);
					}
					Ckmin(ans[L],mn);
				}
			}
		}
	}
	for(int i=0;i<=n;i++){
		if(ans[i]<1e18) printf("%lld ",ans[i]);
		else printf("no ");
	}
	puts("");
}
posted @ 2026-01-20 21:32  XP3301_Pipi  阅读(0)  评论(0)    收藏  举报
Title