【省选模拟】20/06/22

A A A

  • 令两类点集合为 S , T S,T S,T,考虑最后的图一定形如一堆 S , T S,T S,T 的连通块以及若干 S S S 其中每个 S S S 连了若干个 T T T,对这个进行 d p dp dp,记 d p i , j dp_{i,j} dpi,j 表示两类点大小下的图个数,枚举连通块转移, O ( n 4 ) O(n^4) O(n4)
#include
#define cs const
#define pb push_back
using namespace std;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a,b); }
void Mul(int &a, int b){ a = mul(a,b); }
void Dec(int &a, int b){ a = dec(a,b); }
int ksm(int a, int b){ int as=1; for(;b;b>>=1,Mul(a,a)) if(b&1) Mul(as,a); return as; }
cs int N = 105;
int T, n, m, C[N][N], pw[N];
int dp[N][N], f[N][N];
void work(int n){
	for(int i=0; i<=n; i++)C[i][0]=1;
	for(int i=1; i<=n; i++)
	for(int j=1; j<=n; j++)
	C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	pw[0]=1;for(int i=1; i<=n; i++)
	pw[i]=add(pw[i-1],pw[i-1]);
	for(int i=1; i<=n; i++) f[i][0]=1;
	for(int i=1; i<=n; i++)
	for(int j=1; i+j<=n; j++)
	f[i][j]=mul(f[i][j-1],mul(dec(pw[i],1),pw[j-1]));
	for(int i=0; i<=n; i++)
	dp[i][0]=ksm(2,i*(i-1)>>1);
	static int h[N]; h[0]=1;
	for(int i=1; i<=n; i++)
	for(int j=1; j<=i; j++)
	Add(h[i],mul(h[i-j],C[i-1][j-1]));
	for(int i=0; i<=n; i++)
	dp[0][i]=h[i];
	static int z[N]; z[0]=1;
	for(int i=1; i<=n; i++){
		z[i]=dp[i][0];
		for(int j=1; j<i; j++)
		Dec(z[i],mul(C[i-1][j-1],mul(z[j],dp[i-j][0])));
	}
	for(int i=1; i<=n; i++)
	for(int j=1; j<=n; j++)
	Mul(f[i][j],z[i]);
	for(int i=1; i<=n; i++)
	for(int j=1; i+j<=n; j++){
		for(int k=1; k<=i; k++)
		for(int l=1; l<=j; l++)
		Add(dp[i][j],mul(mul(C[i][k],C[j-1][l-1]),mul(dp[i-k][j-l],f[k][l])));
		for(int k=1; k<=j; k++)
		Add(dp[i][j],mul(dp[i][j-k],C[j-1][k-1]));
	}
}
int main(){
	#ifdef FSYolanda
	freopen("1.in","r",stdin);
	#endif
	work(100); scanf("%d",&T); while(T--)
	scanf("%d%d",&n,&m), cout<<dp[n-m][m]<<'\n';
	return 0;
}

B B B

  • 考虑最后分为三类点,中间一段的是有贡献的
    最小割建图,考虑割与 S S S 表示前缀,割 T T T 的表示后缀,拆点,割中间的表示在中间
    对于限制,连边 ( a , b ) , ( a ′ , b ′ ) (a,b),(a',b') (a,b),(a,b) 表示 a a a 的段在 b b b 之前
#include
#define cs const
#define pb push_back
using namespace std;
cs int N = 1e4 + 50;
cs int INF = 1e9 + 7;
int fi[N], nxt[N], to[N], w[N], ec=1;
void adde(int x, int y, int z){
	nxt[++ec]=fi[x], fi[x]=ec, to[ec]=y, w[ec]=z;
	nxt[++ec]=fi[y], fi[y]=ec, to[ec]=x, w[ec]=0;
}
int n, m, a[N], S, T;
int d[N];
bool bfs(){
	memset(d,-1,sizeof(d)); d[S]=0;
	queue<int> q; q.push(S);
	while(!q.empty()){
		int x=q.front(); q.pop();
		for(int e=fi[x],v;e;e=nxt[e])
		if(w[e] && d[v=to[e]]==-1){ 
			d[v]=d[x]+1; q.push(v);
			if(v==T) return true;
		}
	} return false;
}
int dfs(int u, int flw){
	if(u==T) return flw; int ans=0;
	for(int e=fi[u],v;e;e=nxt[e])
	if(d[v=to[e]]==d[u]+1){
		int dlt=dfs(v,min(flw,w[e]));
		w[e]-=dlt; w[e^1]+=dlt;
		ans+=dlt; flw-=dlt; if(!flw) break;
	} if(flw)d[u]=-1; return ans;
}
int dinic(){ int flw=0; while(bfs())flw+=dfs(S,INF); return flw; }
int main(){
	#ifdef FSYolanda
	freopen("1.in","r",stdin);
	#endif
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++)
	scanf("%d",&a[i]);
	S=0, T=n+n+1;
	for(int i=1,u,v; i<=m; i++)
	scanf("%d%d",&u,&v),adde(u,v,INF),adde(u+n,v+n,INF);
	int ans=0;
	for(int i=1; i<=n; i++){
	if(a[i]<0)adde(i,i+n,-a[i]);
	if(a[i]>0)adde(S,i,a[i]),adde(i+n,T,a[i]),ans+=a[i];
	} cout<<ans-dinic()<<'\n';
	return 0;
} 

你可能感兴趣的:(校内模拟)