码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-02-19 23:40:12      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:har   没有   add   处理   答案   inline   long   blog   ||   

原文链接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;
}

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

标签:har   没有   add   处理   答案   inline   long   blog   ||   

原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ39.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!