状压DP杂题

好歹第一次正经学状压,好好总结一下

T1 [CQOI2018] 解锁屏幕

题目传送门

解法

状态设计:

f S , i : 连上了 S 中的所有的点并且当前处于 i 点的方案数 f_{S,i} : 连上了S中的所有的点并且当前处于i点的方案数 fS,i:连上了S中的所有的点并且当前处于i点的方案数

状态转移:

枚举 S S S 补集里的 转移即可,就注意不能越过一个点到另一个点就行

Code

#include 
#include 
#include 
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define _rep(i,j,k) for(int i=k;i>=j;i--)
using ll = long long ; 
using db = double ;
using namespace std;
const int N=20,M=(1<<20);
const ll mod=1e8+7,inv2=499122177;
int n;
ll f[M][N],g[N][N],ans;
pair p[N];
vector vec[N];
ll qpow(ll ba,ll pow) {
    ll res=1; while(pow) {
        if(pow&1) res=res*ba%mod;
        ba=ba*ba%mod,pow>>=1;
    } return res;
}
ll Add(ll x,ll y) { return ((x+y)%mod) ;}
ll Mul(ll x,ll y) { return ((x*y)%mod) ;}
ll Inv(ll x) { return qpow(x,mod-2) ;}
void init() {
    scanf("%d",&n);
    rep(i,1,n) scanf("%d%d",&p[i].first,&p[i].second);
    sort(p+1,p+1+n);
    rep(i,1,n) rep(j,i+1,n) {
        rep(k,i+1,j-1) {
            int tx=p[i].first-p[j].first,ty=p[i].second-p[j].second;
            int px=p[i].first-p[k].first,py=p[i].second-p[k].second;
            if(tx*py==ty*px) g[i][j]|=(1<=4){
        rep(j,1,n)
            if(i&(1<

T2 [NOIP2017 提高组] 宝藏

题目传送门

解法

状态设计:

f S , i :点集为 S ,树高为 i 的最小代价 f_{S,i}:点集为S,树高为i的最小代价 fS,i:点集为S,树高为i的最小代价

状态转移:

枚举 点集 作为最后一层转移即可

Code

#include 
#include 
#include 
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define _rep(i,j,k) for(int i=k;i>=j;i--)
using ll = long long ; 
using db = double ;
using namespace std;
const int N=13,M=(1<<12)+7;
const ll mod=1e8+7,inv2=499122177,INF=1e10;
int n,m;
ll f[N][M],dis[N][M],ans=INF;
ll qpow(ll ba,ll pow) {
    ll res=1; while(pow) {
        if(pow&1) res=res*ba%mod;
        ba=ba*ba%mod,pow>>=1;
    } return res;
}
ll Add(ll x,ll y) { return ((x+y)%mod) ;}
ll Mul(ll x,ll y) { return ((x*y)%mod) ;}
ll Inv(ll x) { return qpow(x,mod-2) ;}
void init() {
    scanf("%d%d",&n,&m);
    rep(i,0,n-1) rep(mask,0,(1<>i&1)) {
        rep(j,0,n-1) if(mask>>j&1) 
			dis[i][mask]=min(dis[i][mask],dis[i][1<>j&1) 
                len+=dis[j][_S];
            f[i][mask]=min(f[i][mask],f[i-1][_S]+i*len);
        }
    }
    rep(i,0,n-1) ans=min(ans,f[i][(1<

T3 [HAOI2016] 字符合并

题目传送门

解法

这题比较有意思,是个区间DP+状压DP

状态设计:

f l , r , m a s k : 区间 [ l , r ] 最后合并成 m a s k 可获得的最大分数 f_{l,r,mask}: 区间[l,r]最后合并成mask可获得的最大分数 fl,r,mask:区间[l,r]最后合并成mask可获得的最大分数

状态转移 :

Code

枚举断点转移即可

#include 
#include 
#include 
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define _rep(i,j,k) for(int i=k;i>=j;i--)
using ll = long long ; 
using db = double ;
using namespace std;
const int N=307,M=(1<<12)+7,K=(1<<8)+7;
const ll mod=1e8+7,inv2=499122177,INF=1e10;
int n,k;
int a[N],c[K];
ll w[K],f[N][N][K],ans=-INF;
ll qpow(ll ba,ll pow) {
    ll res=1; while(pow) {
        if(pow&1) res=res*ba%mod;
        ba=ba*ba%mod,pow>>=1;
    } return res;
}
ll Add(ll x,ll y) { return ((x+y)%mod) ;}
ll Mul(ll x,ll y) { return ((x*y)%mod) ;}
ll Inv(ll x) { return qpow(x,mod-2) ;}
void init() {
    scanf("%d%d",&n,&k);
	rep(i,1,n) scanf("%d",&a[i]);
	rep(i,0,(1<=l;mid-=(k-1)) rep(mask,0,(1< g(2,-INF);
			rep(mask,0,(1<

T4 [NOI2015] 寿司晚宴

技巧性有点大,但不多

解法

将质因子的含有情况存进状态即可,但会 T L E TLE TLE ,所以我们考虑 根号分治 即可

Code

滚动了一下数组

#include 
#include 
#include 
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define _rep(i,j,k) for(int i=k;i>=j;i--)
using ll = long long ; 
using db = double ;
using namespace std;
const int N=507,M=(1<<8)+7,K=(1<<8)+7;
const ll inv2=499122177,INF=1e10;
int n;
int pri[]={0,2,3,5,7,11,13,17,19,0};
ll mod,ans;
ll f[M][M],g1[M][M],g2[M][M];
struct st{
	int val,MAX,mask;
	void init() {
		MAX=-1; int tmp=val;
		rep(i,1,8) {
			// puts("!!");
			if(tmp%pri[i]) continue;
			mask|=(1<<(i-1));
			while((tmp%pri[i]==0)) tmp/=pri[i];
		}
		if(tmp!=1) MAX=tmp;
	}
	bool operator < (const st &x) const { return MAX>=1;
    } return res;
}
ll Add(ll x,ll y) { return ((x+y)%mod) ;}
ll Mul(ll x,ll y) { return ((x*y)%mod) ;}
ll Del(ll x,ll y) { return ((x-y)<0)?x-y+mod:x-y ;}
ll Inv(ll x) { return qpow(x,mod-2) ;}
void init() {
    scanf("%d%lld",&n,&mod); 
	rep(i,2,n) a[i-1].val=i,a[i-1].init();
	sort(a+1,a+n);
	f[0][0]=1;
}
void work() {
	rep(i,1,n-1) {
		if(a[i].MAX!=a[i-1].MAX|| a[i].MAX==-1) {
			rep(mask,0,255) rep(_mask,0,255)
				g1[mask][_mask]=g2[mask][_mask]=f[mask][_mask];
		}
		_rep(mask,0,255) _rep(_mask,0,255) if((mask&_mask)==0){
			if((a[i].mask&mask)==0) g2[mask][_mask|a[i].mask]=Add(g2[mask][_mask|a[i].mask],g2[mask][_mask]);
			if((a[i].mask&_mask)==0) g1[mask|a[i].mask][_mask]=Add(g1[mask|a[i].mask][_mask],g1[mask][_mask]);
		}
		if(i==n-1||a[i].MAX!=a[i+1].MAX|| a[i].MAX==-1) {
			rep(mask,0,255) rep(_mask,0,255) if((mask&_mask)==0) {
				f[mask][_mask]=Add(g1[mask][_mask],Del(g2[mask][_mask],f[mask][_mask]));
			}
		}
	}
	rep(mask,0,255) rep(_mask,0,255) if((mask&_mask)==0)
		ans=Add(ans,f[mask][_mask]);
	printf("%lld\n",ans);
}
int main(){
    init();
	work();
}

T5 [PKUWC2018] 随机算法

题目传送门

解法:

状态设计:

f S : 点集为 S 时的答案 f_{S}:点集为S时的答案 fS:点集为S时的答案

状态转移:

f S = ∑ i ( f S − T i ∗ ( g S − T i + 1 = = g S ) ) ∣ S ∣ g S : S 的最大独立集的点数 T i : i 以及 i 所连的点的集合 f_{S}=\frac{\sum_{i} (f_{S-T_{i}}*(g_{S-T_{i}}+1==g_{S}) )}{|S|}\\ g_{S}:S的最大独立集的点数\\ T_{i}:i以及i所连的点的集合 fS=Si(fSTi(gSTi+1==gS))gS:S的最大独立集的点数Ti:i以及i所连的点的集合

Code

#include 
#include 
#include 
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define _rep(i,j,k) for(int i=k;i>=j;i--)
#define _b_pct  __builtin_popcount 
using ll = long long ; 
using db = double ;
using namespace std;
const int N=21,M=(1<<20)+7,K=(1<<8)+7;
const ll inv2=499122177,INF=1e18,mod=998244353;
int n,m;
int G[N];
ll f[M],g[M];
ll qpow(ll ba,ll pow) {
    ll res=1; while(pow) {
        if(pow&1) res=res*ba%mod;
        ba=ba*ba%mod,pow>>=1;
    } return res;
}
ll Add(ll x,ll y) { return ((x+y)%mod) ;}
ll Mul(ll x,ll y) { return ((x*y)%mod) ;}
ll Inv(ll x) { return qpow(x,mod-2) ;}
void init() {
    scanf("%d%d",&n,&m);
	for(int i=0;i>i&1) {
		g[mask]=max(g[mask],g[mask&(~G[i])]+1);
	}
	f[0]=1;
	// rep(mask,1,(1<>i&1) && (g[mask&(~G[i])]+1==g[mask])) 
			f[mask]=Add(f[mask],f[mask&(~G[i])]) ;
		// printf("%lld %lld %lld\n",f[mask],ll(_b_pct(mask)),Inv(_b_pct(mask)));
		f[mask]=Mul(f[mask],Inv(_b_pct(mask)));
	}
	printf("%lld\n",f[(1<

T6 [ABC152F] Tree and Constraints

题目传送门

解法

容斥即可
式子写一下吧:
A n s = ∑ S ( − 1 ) ∣ S ∣ ∗ f S Ans=\sum_S (-1)^{|S|}*f_{S} Ans=S(1)SfS
至于式子什么意思就自己悟吧

Code

#include 
#include 
#include 
#include 
#include 
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define _rep(i,j,k) for(register int i=k;i>=j;i--)
#define _b_pct  __builtin_popcount 
using ll = long long ; 
using db = double ;
using namespace std;
const int N=107,M=3e3+7,K=(1<<8)+7,INF=1e9;
const ll inv2=499122177,LLF=1e18,mod=998244353;
int n,m,c1;
int head[N],sta[N],tp;
ll f[(1<<20)+7],p[2]={1,-1},ans,p2[M];
bitset bt[22];
struct Edge{ int next,to; }e[M<<1];
void Add_Edge(int fr,int to) {
	e[c1]=(Edge) {head[fr],to};
	head[fr]=c1++;
}
ll qpow(ll ba,ll pow) {
	ll res=1; while(pow) {
		if(pow&1) res=res*ba%mod;
		ba=ba*ba%mod,pow>>=1;
	} return res;
}
ll Add(ll x,ll y) { return ((x+y)%mod) ;}
ll Mul(ll x,ll y) { return ((x*y)%mod) ;}
ll Del(ll x,ll y) { return (x-y<0?x-y+mod:x-y) ;}
ll Inv(ll x) { return qpow(x,mod-2) ;}
ll Block(ll x) {
	ll res=0;
	for(ll l=1,r;l<=x;l=r+1) {
		r=x/(x/l);
		res=Add(res,Mul((x/l),(r-l+1)));
	}
	return res;
}
void dfs(int u,int fa,int po,int id) {
	for(int i=head[u];~i;i=e[i].next) {
		int v=e[i].to; if(v==fa) continue;
		sta[++tp]=i>>1;
		if(v==po) {
			while(tp) bt[id][sta[tp]]=1,tp--;
			return ;
		} 
		dfs(v,u,po,id);
		tp--;
	}

}
void init() {
	scanf("%d",&n);
	p2[0]=1; for(int i=1;i<=n;i++) p2[i]=p2[i-1]<<1;
	memset(head,-1,sizeof head);
	for(int i=1,u,v;i tmp;
		rep(i,0,m-1) if(mask>>i&1) 
			tmp|=bt[i];
		num-=tmp.count();
		ans+=(p[cnt&1]*p2[num]);
	}
	printf("%lld\n",ans);
}
int main(){
    init();
	work();
}

T6 [ARC078F] Mole and Abandoned Mine

题目传送门

解法

就是求割完后满足条件的图的边权和的 最大值
状态的设计是套路的: f S , i f_{S,i} fS,i
转移时考虑图最后的形态一定是

所以删完边后的图一定是由一条从 1 1 1 n n n 的链,和若干个两两之间没有连边,且与链之间仅有一条边的连通块组成 。

分成 连点 或者 连连通块 的情况转移即可
干就完了

Code

#include 
#include 
#include 
#include 
#include 
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define _rep(i,j,k) for(register int i=k;i>=j;i--)
#define _b_pct  __builtin_popcount 
using ll = long long ; 
using db = double ;
using namespace std;
const int N=17,M=(1<<15)+7,INF=1e9;
const ll inv2=499122177,LLF=1e18,mod=998244353;
int n,m,k;
ll w[N][N],f[N][M],g[N][M],h[M],ans;
ll qpow(ll ba,ll pow) {
	ll res=1; while(pow) {
		if(pow&1) res=res*ba%mod;
		ba=ba*ba%mod,pow>>=1;
	} return res;
}
ll Add(ll x,ll y) { return ((x+y)%mod) ;}
ll Mul(ll x,ll y) { return ((x*y)%mod) ;}
ll Del(ll x,ll y) { return (x-y<0?x-y+mod:x-y) ;}
ll Inv(ll x) { return qpow(x,mod-2) ;}
ll Block(ll x) {
	ll res=0;
	for(ll l=1,r;l<=x;l=r+1) {
		r=x/(x/l);
		res=Add(res,Mul((x/l),(r-l+1)));
	}
	return res;
}
void init() {
	scanf("%d%d",&n,&m); k=(1<>i&1)==0){
		rep(j,0,n-1) if(mask>>j&1) 
			g[i][mask]+=w[i][j];
	}
	rep(mask,0,(1<>i&1) && (mask>>j&1)){
		h[mask]+=w[i][j];
	}
	f[0][1]=0;
	rep(mask,0,(1<>i&1) && (mask&1) && f[i][mask]>=0) {
		rep(j,0,n-1) if((mask>>j&1)==0 && w[i][j]>0) {
			f[j][mask|(1<

T7 Favorite Game

题目传送门

这题我就得好好讲讲了

解法

设计状态:

引用他人博客 : 大神的博客

状态转移 :

h , t 的含义分别为到传送门,刺杀地点的最短距离 h,t的含义分别为到传送门,刺杀地点的最短距离 h,t的含义分别为到传送门,刺杀地点的最短距离
1.从传送门到传送门:
枚举传送点转移即可
f m a s k , i + h m a s k , j → f m a s k ∣ j , i f_{mask,i}+h_{mask,j}\to f_{mask|j,i} fmask,i+hmask,jfmaskj,i
2.从传送门到刺杀地点:
i f ( f m a s k , i + t m a s k , j ≤ t i m e j ) i + 1 → g m a s k , j if(f_{mask,i}+t_{mask,j}\le time_j) \quad i+1\to g_{mask,j} if(fmask,i+tmask,jtimej)i+1gmask,j
3.从刺杀地点到传送门:
t i m e i + min ⁡ ( d i s i , j , h m a s k , j ) → f m a s k ∣ j , g m a s k , i time_i+\min(dis_{i,j},h_{mask,j})\to f_{mask|j,g_{mask,i}} timei+min(disi,j,hmask,j)fmaskj,gmask,i
4.从刺杀地点到刺杀地点。
i f ( t i m e j − t i m e i ≥ min ⁡ ( d i s i , j , t m a s k , j ) ) g m a s k , i + 1 → g m a s k , j if(time_j-time_i \ge \min(dis_{i,j},t_{mask,j}))\quad g_{mask,i}+1\to g_{mask,j} if(timejtimeimin(disi,j,tmask,j))gmask,i+1gmask,j

Code

#include 
#include 
#include 
#include 
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define _rep(i,j,k) for(register int i=k;i>=j;i--)
#define _b_pct  __builtin_popcount 
using ll = long long ; 
using db = double ;
using namespace std;
const int N=107,M=(1<<14)+7,K=16,INF=2e9;
const ll inv2=499122177,LLF=1e18,mod=998244353;
int n,m,k;
int f[M][N],g[M][N];//f:时间 ; g:完成数
int h[M][N],t[M][N];//h:到传送门 ; t:到刺杀地点
struct Pos { int xa,ya; } a[N];
struct Qu { int xb,yb,t; } b[N];

bool cmp(Qu x,Qu y) { return x.t>=1;
	} return res;
}

ll Add(ll x,ll y) { return ((x+y)%mod) ;}

ll Mul(ll x,ll y) { return ((x*y)%mod) ;}

ll Del(ll x,ll y) { return (x-y<0?x-y+mod:x-y) ;}

ll Inv(ll x) { return qpow(x,mod-2) ;}

ll Block(ll x) {
	ll res=0;
	for(ll l=1,r;l<=x;l=r+1) {
		r=x/(x/l);
		res=Add(res,Mul((x/l),(r-l+1)));
	}
	return res;
}

int Calc(int xa,int ya,int xb,int yb) { return abs(xa-xb)+abs(ya-yb); }

void init() {
	scanf("%d%d",&n,&m); k=(1<>(j-1)&1) 
			h[mask][i]=min(h[mask][i],Calc(a[i].xa,a[i].ya,a[j].xa,a[j].ya));
	}
	rep(mask,0,k) rep(i,1,m) {
		rep(j,1,n) if(mask>>(j-1)&1) 
			t[mask][i]=min(t[mask][i],Calc(b[i].xb,b[i].yb,a[j].xa,a[j].ya));
	}
	int ans=1;
	rep(mask,0,k) {
		rep(i,0,m) if(f[mask][i]!=INF) {
			rep(j,1,n) if((mask>>(j-1)&1)==0) //f->f
				f[mask|(1<<(j-1))][i]=min(f[mask|(1<<(j-1))][i],f[mask][i]+h[mask][j]);
			rep(j,1,m) if(b[j].t>=f[mask][i]+t[mask][j]) // f->g
				g[mask][j]=max(g[mask][j],i+1),ans=max(ans,g[mask][j]);
		}
		rep(i,1,m) if(g[mask][i]!=-INF) {
			rep(j,1,n) // g->f
				f[mask|(1<<(j-1))][g[mask][i]]=min(f[mask|(1<<(j-1))][g[mask][i]], b[i].t+min(Calc(a[j].xa,a[j].ya,b[i].xb,b[i].yb),h[mask][j]) );
			rep(j,i+1,m) if(min(t[mask][j],Calc(b[j].xb,b[j].yb,b[i].xb,b[i].yb))<=b[j].t-b[i].t) // g->g
				g[mask][j]=max(g[mask][j],g[mask][i]+1),ans=max(ans,g[mask][j]);
		}
		// printf("%d %d:\n%d %d\n",mask,i,f[mask][i],g[mask][i]);
	}
	printf("%d\n",ans);
}	
int main(){
    init();
	work();
}

你可能感兴趣的:(算法,动态规划,状压DP)