状压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<

\[ \ \]

\[ \ \]

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

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

int d[30][30];
#include
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  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]]>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<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<>1)|(1<<(p-1))]]++;
        } else {
            rep(i,0,p-1) if(S&(1<>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<>=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<

\[ \ \]

\[ \ \]

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<>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);
}
 
 

你可能感兴趣的:(状压dp专题复习)