zoukankan      html  css  js  c++  java
  • UOJ#39. 【清华集训2014】简单回路 动态规划 插头DP

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

    前言

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

    题解

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

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

    我们发现,(m=6) 时,不同的插头状态数只有 (C_1inom 62 + C_2inom 64 + C_3 inom 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
    ",ans[x][y]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    不用第三个变量互换两变量的值的两种方法。
    struts2截取字符串
    js控制表格单双行颜色交替显示
    Shell字符串
    Shell数组:shell数组的定义、数组长度
    Shell注释
    Shell运算符:Shell算数运算符、关系运算符、布尔运算符、字符串运算符等
    Shell替换:Shell变量替换,命令替换,转义字符
    Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数
    Shell变量:Shell变量的定义、删除变量、只读变量、变量类型
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ39.html
Copyright © 2011-2022 走看看