• 状压dp专题复习


    状压dp专题复习

    (有些题过于水,我直接跳了)

    技巧总结 :

    1.矩阵状压上一行的选择情况 \(n * 2^n\)

    D [BZOJ2734][HNOI2012]集合选数

    蒻得不行的我觉得这是一道比较难的题,以至于我卡了很久

    可以看出,所有会互相直接造成影响的数之间构成一张\(DAG\),边就是\(i->i*2,i->i*3\)

    取出每一个连通块之后,就是一个独立集个数的问题

    \(DAG\)还可以求独立集?

    我们其实可以惊人得发现,这张\(DAG\)过于整齐,就是一个网格图,就是一张网格图上相邻的点不能取的问题

    这个,状压矩阵即可

    
    const int N=1e5+10,P=1e9+1;
     
     
     
    int n;
    ll dp[20][1<<11];
    int A[20];
     
    ll Solve(int i){ 
        int t=0;
        A[0]=0;
        dp[0][0]=1;
        for(;i<=n;i*=2) {//网格图的列数
            t++;
            A[t]=0;
            int c=0;
            for(int j=i;j<=n;j*=3) A[t]|=1<<(c++); //取出网格图这一行的大小
            rep(j,0,A[t]) dp[t][j]=0;
            rep(S1,0,A[t-1]) {
                int fl=1;
                rep(j,0,c+1) if((S1&(1<<j)) && (S1&(1<<(j+1)))) fl=0;
                if(!fl) continue;
                int R=A[t]^(S1&A[t]);
                for(reg int S2=R;;S2=(S2-1)&R) {
                    int fl=1;
                    rep(j,0,c-1) if((S2&(1<<j)) && (S2&(1<<(j+1)))) fl=0;
                    if(!fl) {
                        if(!S2) break;
                        continue;
                    }
                    (dp[t][S2]+=dp[t-1][S1])%=P;
                    if(!S2) break;
                }
            }
        }
        ll res=0;
        rep(i,0,A[t]) res+=dp[t][i];
        res%=P;
        return res;
    }
     
     
     
    int main(){
        n=rd();
        ll ans=1;
        rep(i,1,n) {
            if(i%2==0||i%3==0) continue;
            //这是一个联通块
            ans=ans*Solve(i)%P;
        }
        printf("%lld\n",ans);
    }
     
    

    \[\ \]

    \[\ \]

    G [BZOJ1097] [POI2007]旅游景点atr

    预处理前面k个点之间的dis,然后就像是一个TSP一样,但是有限制

    int d[30][30];
    #include<bits/stdc++.h>
    using namespace std;
     
    #define reg register
    //typedef long long ll;
    typedef int ll;
    #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
     
    char IO;
     
    int rd(){
        int s=0,f=0;
        while(!isdigit(IO=getchar())) if(IO=='-') f=1;
        do s=(s<<1)+(s<<3)+(IO^'0');
        while(isdigit(IO=getchar()));
        return f?-s:s;
    }
     
    const int N=20010,M=2e5+10;
     
    #pragma GCC optimize(3)
    #pragma GCC optimize(2)
    #define chk(a,b) ((a>b)&&(a=b))
     
    int n,m,k;
     
     
    ll dis[N];
     
    struct Node{ 
        int x;
        ll d;
        int operator < (const Node __) const{
            return x<__.x;
        }
    };
    vector <Node> G[N];
     
     
    int s[N<<2];
     
    int pos[N];
     
     
    inline void push(reg int x) {
        int p=pos[x];
        while(p>1) {
            p>>=1;
            if(dis[s[p<<1]]<dis[s[p<<1|1]]) s[p]=s[p<<1];
            else s[p]=s[p<<1|1];
        }
    }
     
    inline int top(){
        reg int x=s[1];
        s[pos[x]]=0;
        push(x);
        return x;
    }
     
    void Build(int p,int l,int r) {
        if(l==r) {
            pos[l]=p;
            return;
        }
        reg int mid=(l+r)>>1;
        Build(p<<1,l,mid);
        Build(p<<1|1,mid+1,r);
    }
    //用线段树实现堆的功能
     
    void GetDis(int st,int R) {
        memset(dis,63,sizeof dis); dis[st]=0;
        for(reg int i=1;i<=n;++i) s[pos[i]]=i;
        push(st);
        int cnt=0;
        while(dis[s[1]]<=1e9) {
            reg int u=top();
            if(u<=R && (++cnt>=R) ) return;
            rep(i,0,G[u].size()-1) {
                int v=G[u][i].x,w=G[u][i].d;
                if(dis[v]<=dis[u]+w) continue;
                dis[v]=dis[u]+w;
                push(v);
            }
        }
    }
     
    int fa[35];
    int dp[1<<20][22];
    int Log[1<<21];
     
    int tmp[35];
     
    int main(){
        n=rd(),m=rd();
        k=rd()+1;
        rep(i,0,21) Log[1<<i]=i;
        rep(i,1,m) {
            int u=rd(),v=rd(),w=rd();
            G[u].push_back((Node){v,w});
            G[v].push_back((Node){u,w});
        }
        int q=rd();
        rep(i,1,q) {
            int u=rd(),v=rd();
            fa[v]|=1<<(u-2);
        }
        Build(1,1,n);
        rep(i,1,k) {
            GetDis(i,k);
            rep(j,1,k) d[i][j]=dis[j];
        }
        GetDis(n,n);
        int A=(1<<(k-1))-1;
        memset(dp,63,sizeof dp);
        dp[0][1]=0;
        for(reg int S=0;S<A;++S) {
            reg int T=S;
            int cnt=0;
            rep(j,0,k-2) if(~S&(1<<j)) if((fa[j+2]&S)==fa[j+2]) tmp[++cnt]=j+2;
            while(T) {
                reg int i=Log[T&-T]+2;
                T&=T-1;
                if(dp[S][i]>1e9) continue;
                rep(k,1,cnt) {
                    int j=tmp[k];
                    reg int NS=S|(1<<(j-2));
                    chk(dp[NS][j],dp[S][i]+d[i][j]);
                }
            }
            do {
                reg int i=1;
                if(dp[S][i]>1e9) continue;
                rep(k,1,cnt) {
                    int j=tmp[k];
                    reg int NS=S|(1<<(j-2));
                    chk(dp[NS][j],dp[S][i]+d[i][j]);
                }
            } while(0);
        }
        ll ans=1e9;
        rep(i,1,k) ans=min(ans,dp[A][i]+dis[i]);
        printf("%d\n",ans);
    }
     
    

    \[\ \]

    \[\ \]

    H [BZOJ2004] [Hnoi2010]Bus 公交线路

    \(n\)\(10^9\)了,还不矩阵吗?

    \(dp[S]\)表示前\(p\)位哪些点放了车,不过状态显然保证\(popcount(S)==k\)

    然后由于状态最多其实是\(C_{10}^{5}=252\)所以可以跑矩阵

     
    bool be;
     
    int n,p,k;
     
    int dp[1<<10];
    int cnt[1<<10];
    int tmp[1<<10];
    int A;
     
     
    int st[300],sc,id[1<<10];
    int f[1][300],ans[1][300];
     
     
    int B;
     
     
    struct Mat{
        int a[300][300];
        void init(){ memset(a,0,sizeof a); }
        void Get1(){ rep(i,1,sc) a[i][i]=1; }
        Mat operator * (const Mat x) const {
            Mat res;
            for(reg int i=1;i<=sc;++i) {
                for(reg int j=1;j<=sc;++j) {
                    ll t=0;
                    for(reg int o=1;o<=sc;++o) t+=a[i][o]*x.a[o][j];
                    res.a[i][j]=t%P;
                }
            }
            return res;
        }
    }res,x;
     
     
     
     
    void Solve(){
        A=(1<<p)-1;
        rep(i,1,A) cnt[i]=cnt[i&(i-1)]+1;
        rep(S,0,A) if(cnt[S]==k) ++sc,id[st[sc]=S]=sc;
        rep(S,0,A) if(cnt[S]==k) {
            if(S&1) {
                x.a[id[S]][id[(S>>1)|(1<<(p-1))]]++;
            } else {
                rep(i,0,p-1) if(S&(1<<i)) {
                    int NS=((S^(1<<i))>>1)|(1<<(p-1));
                    x.a[id[S]][id[NS]]++;
                }
            }
        }
        int T=0;
        for(reg int j=p-1;j>=p-k;j--) T|=1<<j;
        f[0][id[T]]=1;
        res.Get1();
        n-=k;
        int t=n;
        while(t) {
            if(t&1) res=res*x;
            x=x*x;
            t>>=1;
        }
        rep(i,0,0) rep(j,1,sc) rep(o,1,sc) (ans[i][o]+=f[i][j]*res.a[j][o])%=P;
        T=0;
        rep(j,p-k,p-1) T|=1<<j;
        printf("%d\n",ans[0][id[T]]);
    }
     
    bool ed;
     
    int main(){
        //printf("%.2lf\n",(&ed-&be)/1024.0/1024.0);
        n=rd(),k=rd(),p=rd();
        Solve();
    }
     
     
     
     
     
    

    \[\ \]

    \[\ \]

    L [BZOJ3195] [Jxoi2012]奇怪的道路

    题目限定了距离,所以直接dp选了几条边,之前的点每个点的边数是不是奇数

     
    
    const int N=80,P=1000000007;
     
     
    int n,m,k;
     
    ll dp[31][1<<8][31];
    ll C[N][N];
     
     
     
    int main(){
        n=rd(),m=rd(),k=rd();
        k=min(k,n);
        C[0][0]=1;
        rep(i,1,N-1) {
            C[i][0]=1;
            rep(j,1,N-1) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
        }
        dp[0][0][0]=1;
        rep(i,1,k-1) {
            int A=(1<<i)-1;
            rep(S,0,A) {
                int t=0;
                rep(j,0,i-1) if(S&(1<<j)) t++;
                rep(R,0,A) {
                    int NS=(S^R)|((t&1)<<i);
                    rep(j,0,m) {
                        for(reg int d=j+t;d<=m;d+=2) {
                            (dp[i][NS][d]+=C[(d-j-t)/2+i-1][i-1]*dp[i-1][R][j]%P)%=P;
                        }
                    }
                }
            }
        }
        int A=(1<<k)-1;
        rep(i,k,n-1) {
            rep(S,0,A) {
                int t=0;
                rep(j,0,k-1) if(S&(1<<j)) t++;
                rep(R,0,A) if((R&1)==(S&1)) {
                    int NS=((S^R)>>1)|((t&1)<<(k-1));
                    rep(j,0,m) {
                        for(reg int d=j+t;d<=m;d+=2) {
                            (dp[i][NS][d]+=C[(d-j-t)/2+k-1][k-1]*dp[i-1][R][j]%P)%=P;
                        }
                    }
                }
            }
        }
        ll ans=dp[n-1][0][m];
        printf("%lld\n",ans);
    }
     
     
    
  • 相关阅读:
    Sqoop相关
    Hive桶表
    Hive视图
    Hive的Explain命令
    Django路由分发
    Django对应的路由名称
    Django基于正则表达式的URL(2)
    Django基于正则表达式的URL(1)
    Django模板语言循环字典
    Django的CBV和FBV
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11665098.html
Copyright © 2020-2023  润新知