zoukankan      html  css  js  c++  java
  • 状压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);
    }
     
     
    
  • 相关阅读:
    算法训练 表达式计算
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    New ways to verify that Multipath TCP works through your network
    TCP的拥塞控制 (Tahoe Reno NewReno SACK)
    Multipath TCP Port for Android 4.1.2
    How to enable ping response in windows 7?
    NS3
    Multipath TCP Port for Android
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11665098.html
Copyright © 2011-2022 走看看