Loading

正睿 2021 Noip 十连测 Day2

A

写个暴力找规律即可。

B

\(80\) 分做法:数位 dp,设 \(f_{i,j}\) 为低 \(i\) 位,\(\bmod a=j\) 是否可行,并记录一下转移。

考虑建一张 \(a\) 个点 \(0\sim(a-1)\) 的图,对于每个合法的转移 \(x\to (10x+y)\bmod a\) 连边,最小位数就是任意非零点到 \(0\) 号点的最短路,可以直接 bfs 出来。

然后贪心地从高位到低位确定即可。

C

注意到任意选边都能构成完美匹配的条件,是原图的每个极大连通块都是左右部点数相同的完全二分图。

于是问题变成了:给你若干个连通块,你需要合并其中一些,使得每个连通块内左右部点数相同,且所有连通块左部点数的平方和最小。

因为 \(n\le 25\),考虑大力状压:把左右部点数相同的先去掉,然后记录左右部的孤点各有多少个,以及剩下的连通块。

\(f_{S,i,j}\) 为把 \(S\) 内的所有连通块拼成若干个左右部点数相同的连通块,并且使用了 \(i\) 个左部孤点,\(j\) 个右部孤点,平方和最小是多少。转移时枚举 \(S\) 的一个子集,强制配上一些孤点使得这个子集并成一个左右部点数相同的连通块,贡献到 \(f\) 上即可。

复杂度:考虑最多形成多少个连通块?可以发现左部一个点、右部两个点(或者反过来)能形成 \(2/3n\) 个连通块。于是复杂度是 \(\mathcal{O}(3^{2/3n}n^2)\)。算一下可以发现这玩意最多 \(4\times 10^{10}\),但连通块个数越多,孤点个数越小,所以远远跑不满。

代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &x){
	x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	x=x*_f;
}
template<typename T,typename... Args> void Read(T &x,Args& ...others){
	Read(x);Read(others...);
}
typedef long long ll;
const int N=27,M=17;
int T,n;char a[N][N];
struct DSU{
	int fa[N<<1];
	int Find(int p){
		return fa[p]==p?p:fa[p]=Find(fa[p]);
	}
	void Union(int p,int q){
		fa[Find(p)]=Find(q);
	}
};
int dots[2][1<<M],dot[2],siz[2][N<<1],cnt,bl[2][N<<1],cost[1<<M],f[1<<M][N][N];
DSU dsu;
int main(){
	Read(T);
	while(T--){
		memset(dots,0,sizeof dots);
		memset(siz,0,sizeof siz);
		cnt=dot[0]=dot[1]=0;
		memset(bl,0,sizeof bl);
		memset(cost,0,sizeof cost);
		Read(n);
		For(i,1,n*2) dsu.fa[i]=i;
		int ans=0;
		For(i,1,n){
			scanf("%s",a[i]+1);
			For(j,1,n) if(a[i][j]=='1') dsu.Union(i,j+n),--ans;
		}
		For(i,1,n*2){
			++siz[i>n][dsu.Find(i)];
		}
		For(i,1,n*2){
			if(dsu.Find(i)!=i) continue;
			if(siz[0][i]==siz[1][i]) ans+=siz[0][i]*siz[1][i];
			else if(siz[0][i]+siz[1][i]==1) ++dot[siz[1][i]];
			else{
				++cnt;
				bl[0][cnt]=siz[0][i],bl[1][cnt]=siz[1][i];
			}
		}
		int S=(1<<cnt)-1;
		For(st,1,S){
			for(int i=1;i<=cnt;++i){
				if((st&1<<(i-1))==0) continue;
				dots[0][st]+=bl[0][i],dots[1][st]+=bl[1][i];
			}
			if(dots[0][st]>dots[1][st]){
				cost[st]=dots[0][st]*dots[0][st];
				dots[1][st]=dots[0][st]-dots[1][st],dots[0][st]=0;
			}else{
				cost[st]=dots[1][st]*dots[1][st];
				dots[0][st]=dots[1][st]-dots[0][st],dots[1][st]=0;
			}
		}
		For(i,0,S) For(j,0,dot[0]) For(k,0,dot[1]) f[i][j][k]=0x3f3f3f3f;
		f[0][0][0]=0;
		For(i,1,S){
			for(int st=i;st;st=(st-1)&i){
				For(j,dots[0][st],dot[0]){
					For(k,dots[1][st],dot[1]){
						f[i][j][k]=min(f[i][j][k],f[i^st][j-dots[0][st]][k-dots[1][st]]+cost[st]);
					}
				}
			}
		}
		int res=0x3f3f3f3f;
		For(i,0,min(dot[0],dot[1])){
			res=min(res,f[S][dot[0]-i][dot[1]-i]+i);
		}
		printf("%d\n",res+ans);
	}
	return 0;
}
posted @ 2021-09-23 22:38  Alan_Zhao_2007  阅读(110)  评论(0编辑  收藏  举报