UOJ#39. 【清华集训2014】简单回路 动态规划 插头DP

原文链接www.cnblogs.com/zhouzhendong/p/UOJ39.html

前言

老年选手没有码力。太久没更博了强行更一发。

题解

这题一看就是个插头DP,于是我们考虑用括号序列来表示状态。

关于插头DP,推荐一篇博客:https://www.cnblogs.com/zinthos/p/3897854.html

我们发现,\(m=6\) 时,不同的插头状态数只有 \(C_1\binom 62 + C_2\binom 64 + C_3 \binom 66 = 50\) 个。(其中 \(C_x\)\(x\) 对括号组成的可能的括号序列数)

\(pre_{i,S}\) 表示前 \(i\) 行,没有闭环,第 \(i\) 行的插头情况为 \(S\) 的方案数。

\(suf_{i,S}\) 表示后 \(n-i+1\) 行,没有闭环,第 \(i\) 行的插头情况为 \(S\) 的方案数。

\(ans_{i,j}\) 表示 \((i,j)\)\((i+1,j)\) 之间的竖边的答案。

那么,假设 \(S,T\) 是一对拼在一起刚好是一个环的插头状态,那么,如果 \((i,j)\leftrightarrow (i+1,j)\) 是某 \(S,T\) 的插头衔接处的竖边,那么 \(ans_{i,j}\) 就包含了相应的 \(pre_{i,S}\cdot suf_{i+1,T}\) 种方案。

接下来考虑如何求 \(pre\)\(suf\)

考虑预处理转移,设当前插头为 \(S\),枚举新的一行中有哪些横边被经过,然后计算出转移到的状态即可。

具体实现过于复杂,只能用 \(s**\ t\) 来形容。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=(a);i<=(b);i++)
#define Fod(i,b,a) for (int i=(b);i>=(a);i--)
#define fi first
#define se second
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
using namespace std;
typedef long long LL;
typedef pair <int,int> pii;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f=ch=='-',ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=1005,M=10,S=55,mod=1e9+7;
void Add(int &x,int y){
	if ((x+=y)>=mod)
		x-=mod;
}
int n,m;
int a[N];
void in_a(){
	int k=read(),x,y;
	while (k--)
		x=read(),y=read(),a[x]|=1<<y;
}
int id[N],val[S][M],scnt=0,can[S];
vector <pii> trans[S];
vector <int> opp[S];
void getmatch(int *s,int *p){
	static int st[M];
	int top=0;
	For(i,1,m)
		if (s[i]==1)
			st[++top]=i;
		else if (s[i]==-1)
			p[i]=st[top],p[st[top]]=i,top--;
}
void get_trans(int x,int e){
	static int f[M],p[M],d[M],res[M],vis[M];
	clr(f),clr(p),clr(res),clr(vis);
	int *s=val[x];
	For(i,1,m-1)
		f[i]=e>>(i-1)&1;
	int zzy=0;
	For(i,1,m){
		d[i]=(s[i]!=0)+f[i-1]+f[i];
		if (s[i]!=0&&f[i-1]&&f[i])
			return;
		if (d[i])
			zzy|=1<<i;
	}
	getmatch(s,p);
	For(i,1,m){
		if (vis[i]||d[i]!=1)
			continue;
		int j=i;
		while (j==i||d[j]==2){
			int nxt=0;
			vis[j]=1;
			if (p[j]&&!vis[p[j]])
				nxt=p[j];
			else if (f[j-1]&&!vis[j-1])
				nxt=j-1;
			else if (f[j]&&!vis[j+1])
				nxt=j+1;
			if (!nxt)
				return;
			j=nxt;
		}
		vis[j]=1;
		res[min(i,j)]=1,res[max(i,j)]=-1;
	}
	For(i,1,m)
		if (d[i]&&!vis[i])
			return;
	int Res=0;
	Fod(i,m,1)
		Res=Res*3+res[i]+1;
	trans[x].pb(mp(id[Res],zzy));
}
int check_opposite(int *a,int *b){
	static int pa[M],pb[M],vis[M];
	clr(vis);
	For(i,1,m)
		if ((a[i]!=0)^(b[i]!=0))
			return 0;
	getmatch(a,pa),getmatch(b,pb);
	int cnt=0;
	For(i,1,m)
		if (a[i]!=0)
			cnt++;
	For(i,1,m)
		if (a[i]!=0){
			int x=i;
			For(t,1,cnt){
				vis[x]=1,x=(t&1?pa:pb)[x];
				if (t<cnt&&vis[x])
					return 0;
			}
			return 1;
		}
}
void getval(int m){
	static int tmp[M];
	int p3=pow(3,m);
	For(i,0,p3-1){
		clr(tmp);
		int t=i,s=0,flag=1,empty=1,Can=0;
		For(j,1,m){
			tmp[j]=t%3-1;
			if (s==1&&Can!=-1)
				Can|=1<<j;
			s+=tmp[j];
			if (s==1&&Can!=-1)
				Can|=1<<j;
			if (s>1)
				Can=-1;
			empty&=s==0;
			flag&=s>=0;
			t/=3;
		}
		flag&=s==0;
		flag&=!empty;
		if (flag){
			id[i]=++scnt;
			For(j,1,m)
				val[scnt][j]=tmp[j];
			can[scnt]=Can;
		}
	}
	For(i,1,scnt){
		For(e,0,(1<<(m-1))-1)
			get_trans(i,e);
		For(j,1,scnt)
			if (check_opposite(val[i],val[j]))
				opp[i].pb(j);
	}
}
int pre[N][S],suf[N][S];
int ans[N][M];
int main(){
	n=read(),m=read();
	in_a();
	getval(m);
	For(i,1,n){
		For(j,1,scnt){
			if (can[j]!=-1&&!(can[j]&a[i]))
				Add(pre[i][j],1);
			int v=pre[i-1][j];
			if (!v)
				continue;
			for (auto t : trans[j])
				if (!(t.se&a[i]))
					Add(pre[i][t.fi],v);
		}
	}
	Fod(i,n,1){
		For(j,1,scnt){
			if (can[j]!=-1&&!(can[j]&a[i]))
				Add(suf[i][j],1);
			int v=suf[i+1][j];
			if (!v)
				continue;
			for (auto t : trans[j])
				if (!(t.se&a[i]))
					Add(suf[i][t.fi],v);
		}
	}
	For(i,1,n-1)
		For(j,1,scnt)
			for (auto o : opp[j]){
				int v=(LL)pre[i][j]*suf[i+1][o]%mod;
				For(k,1,m)
					if (val[j][k]!=0)
						Add(ans[i][k],v);
			}
	int q=read();
	while (q--){
		int x=read(),y=read();
		printf("%d\n",ans[x][y]);
	}
	return 0;
}
posted @ 2020-02-19 23:01  zzd233  阅读(573)  评论(0编辑  收藏  举报