【做题记录】csp2025-状压dp

A. [USACO13NOV] No Change G
\(dp[S]\) 表示取的硬币状态为 \(S\) 时最多买多少东西。给 \(n\) 个物品做前缀和,转移二分即可。时间复杂度 \(O(2^kk\log n)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define int ll
#define uprb upper_bound
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5,maxm=(1<<16)+5;
int n,m,a[20],b[maxn],dp[maxm];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
	read(m)read(n);
	for(int i=1;i<=m;i++){
		read(a[i]);
	}
	for(int i=1;i<=n;i++){
		read(b[i]);
		b[i]+=b[i-1];
	}
	int ans=-1;
	for(int S=0;S<1<<m;S++){
		int tmp=0;
		for(int i=1;i<=m;i++){
			if(S>>(i-1)&1){
				continue;
			}
			tmp+=a[i];
			int nS=S|1<<(i-1);
			int p=uprb(b+1,b+n+1,b[dp[S]]+a[i])-b-1;
			dp[nS]=max(dp[nS],p);
		}
		if(dp[S]==n){
			ans=max(ans,tmp);
		}
	}
	printf("%lld",ans);
	return 0;
}
}
signed main(){return asbt::main();}

B. [HAOI2007] 修筑绿化带
好好好,根本不是状压DP……
思路像理想的正方形。考虑对于一个固定的 \(A\times B\) 的矩形,答案一定是矩形的和减去它里面最小的 \(C\times D\) 的矩形的和。前者二维前缀和即可,后者需要类似于那道题搞两次单调队列。
具体地,设 \(B[i][j]\) 表示以 \((i,j)\) 为右下角的 \(C\times D\) 的矩形的和,设 \(P[i][j]\) 表示以 \((i,j)\) 为右下角的 \(A\times B\) 的矩形中,右下角和 \((i,j)\) 在同一行的 \(C\times D\) 的所有矩形中最小的一个。这需要跑一次单调队列。然后再对 \(P\) 类似地纵向求一次单调队列得到 \(Q\)\(Q\) 即为所求。
需要注意这样一句话:

同时在花坛四周修建一片绿化带,让花坛被绿化带围起来。

也就是说,花坛是不能贴着绿化带的边的。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e3+5,inf=0x3f3f3f3f;
typedef int juz[maxn][maxn];
int n,m,a,b,c,d,q[maxn];
juz f,A,B,P,Q;
il int sum(int x1,int y1,int x2,int y2){
	return f[x2][y2]-f[x1-1][y2]-f[x2][y1-1]+f[x1-1][y1-1];
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n)read(m)read(a)read(b)read(c)read(d);
	memset(A,-0x3f,sizeof A);
	memset(B,0x3f,sizeof B);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			read(f[i][j]);
			f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
			if(i>=a&&j>=b){
				A[i][j]=sum(i-a+1,j-b+1,i,j);
			}
			if(i>=c&&j>=d){
				B[i][j]=sum(i-c+1,j-d+1,i,j);
			}
		}
	}
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=m;j++){
//			cout<<A[i][j]<<" ";
//		}
//		puts("");
//	}
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=m;j++){
//			cout<<B[i][j]<<" ";
//		}
//		puts("");
//	}
	memset(P,0x3f,sizeof P);
	for(int i=1,hd,tl;i<=n;i++){
		hd=1,tl=0;
		q[1]=0;
		for(int j=1;j<=m;j++){
			while(hd<=tl&&q[hd]<j-b+d+1){
				hd++;
			}
			P[i][j]=B[i][q[hd]];
			while(hd<=tl&&B[i][q[tl]]>B[i][j]){
				tl--;
			}
			q[++tl]=j;
		}
	}
	memset(Q,0x3f,sizeof Q);
	for(int j=1,hd,tl;j<=m;j++){
		hd=1,tl=0;
		q[1]=0;
		for(int i=1;i<=n;i++){
			while(hd<=tl&&q[hd]<i-a+c+1){
				hd++;
			}
			Q[i][j]=P[q[hd]][j];
			while(hd<=tl&&P[q[tl]][j]>P[i][j]){
				tl--;
			}
			q[++tl]=i;
		}
	}
	int ans=-inf;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			ans=max(ans,A[i][j]-Q[i][j]);
		}
	}
	printf("%d",ans);
	return 0;
}
}
int main(){return asbt::main();}

C. [COCI2016-2017#1] Vještica
6,其实很简单。
考虑将两个字符串集合合并时,新的集合的答案,其实就是两个集合的答案之和再减去公共的字符数量。
原因是新的公共字符构成的集合绝对是旧的公共字符集合的子集,换句话说一定能覆盖。于是转移是简单的。
转移一定要枚举每个子集,原因是简单地加入一个字符串是没有覆盖所有情况的。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e6+5,maxm=(1<<16)+5,inf=0x3f3f3f3f;
int n,cnt[20][30],dp[maxm],hp[30];
char s[maxn];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n);
	memset(dp,0x3f,sizeof dp);
	for(int i=1;i<=n;i++){
		scanf(" %s",s+1);
		int S=1<<(i-1);
		dp[S]=1;
		for(int j=1;s[j]!='\0';j++){
			cnt[i][s[j]-'a']++;
			dp[S]++;
		}
	}
	for(int S=1;S<1<<n;S++){
		for(int i=0;i<=25;i++){
			hp[i]=inf;
		}
		for(int i=1;i<=n;i++){
			if(S>>(i-1)&1){
				for(int j=0;j<=25;j++){
					hp[j]=min(hp[j],cnt[i][j]);
				}
			}
		}
		int tmp=1;
		for(int i=0;i<=25;i++){
			tmp+=hp[i];
		}
		for(int nS=S&(S-1);nS;nS=(nS-1)&S){
			dp[S]=min(dp[S],dp[nS]+dp[S^nS]-tmp);
		}
	}
	printf("%d",dp[(1<<n)-1]);
	return 0;
}
}
int main(){return asbt::main();}

D. New Year Tree
一眼线段树。
具体地,先跑一遍dfn,然后维护区间颜色集合。
显然它又不是状压DP
记得1ll

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define pb push_back
#define popcntll __builtin_popcountll
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=4e5+5;
int n,m,a[maxn],cnt;
int dfn[maxn],sz[maxn],zhan[maxn];
vector<int> e[maxn];
il void dfs(int u,int fa){
	dfn[u]=++cnt;
	zhan[cnt]=u;
	sz[u]=1;
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs(v,u);
		sz[u]+=sz[v];
	}
}
struct stree{
	int tag[maxn<<2];
	ll zhi[maxn<<2];
	il void pushup(int id){
		zhi[id]=zhi[lid]|zhi[rid];
	}
	il void pushdown(int id){
		if(tag[id]){
			tag[lid]=tag[rid]=tag[id];
			zhi[lid]=zhi[rid]=1ll<<(tag[id]-1);
			tag[id]=0;
		}
	}
	il void build(int id,int l,int r){
		if(l==r){
			zhi[id]=1ll<<(a[zhan[l]]-1);
			return ;
		}
		int mid=(l+r)>>1;
		build(lid,l,mid);
		build(rid,mid+1,r);
		pushup(id);
	}
	il void upd(int id,int L,int R,int l,int r,int val){
		if(L>=l&&R<=r){
			tag[id]=val;
			zhi[id]=1ll<<(val-1);
			return ;
		}
		pushdown(id);
		int mid=(L+R)>>1;
		if(l<=mid){
			upd(lid,L,mid,l,r,val);
		}
		if(r>mid){
			upd(rid,mid+1,R,l,r,val);
		}
		pushup(id);
	}
	il ll query(int id,int L,int R,int l,int r){
		if(L>=l&&R<=r){
			return zhi[id];
		}
		pushdown(id);
		int mid=(L+R)>>1;
		ll res=0;
		if(l<=mid){
			res|=query(lid,L,mid,l,r);
		}
		if(r>mid){
			res|=query(rid,mid+1,R,l,r);
		}
		return res;
	}
}SG;
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n)read(m);
	for(int i=1;i<=n;i++){
		read(a[i]);
	}
	for(int i=1,u,v;i<n;i++){
		read(u)read(v);
		e[u].pb(v),e[v].pb(u);
	}
	dfs(1,0);
	SG.build(1,1,n);
	while(m--){
		int opt,u,x;
		read(opt)read(u);
		switch(opt){
			case 1:{
				read(x);
				SG.upd(1,1,n,dfn[u],dfn[u]+sz[u]-1,x);
				break;
			}
			default:{
				printf("%d\n",popcntll(SG.query(1,1,n,dfn[u],dfn[u]+sz[u]-1)));
				break;
			}
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

E. Yet Another Substring Reverse
因为字符不重复,因此不用考虑它们的排列顺序,即翻转子串就是将两个不交的子串连到一块。这里不交既指位置不交又指字符集不交。但显然字符集不交则一定位置不交。因此只用考虑对每一个字符集处理最大长度。高维前缀最大值即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e6+5,maxm=(1<<20)+5;
int n,dp[maxm];
char s[maxn];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	scanf(" %s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;i++){
		for(int j=i,S=0;j;j--){
			if(S>>(s[j]-'a')&1){
				break;
			}
			S|=1<<(s[j]-'a');
			dp[S]=i-j+1;
		}
	}
	for(int i=1;i<=20;i++){
		for(int S=0;S<1<<20;S++){
			if(S>>(i-1)&1){
				continue;
			}
			dp[S|1<<(i-1)]=max(dp[S|1<<(i-1)],dp[S]);
		}
	}
	int ans=0;
	for(int S=1;S<=(1<<20)-2;S++){
		ans=max(ans,dp[S]+dp[((1<<20)-1)^S]);
	}
	printf("%d",ans);
	return 0;
}
}
int main(){return asbt::main();}

F. [TJOI2015]棋盘
重中之重:

编号从 \(0\) 开始,即第 \(1\) 行指的是中间那行。

\(dp[i][S]\) 表示第 \(i\) 行放置棋子状态为 \(S\) 的方案数。发现时间和空间都无法接受,容易想到矩阵加速。代码十分好敲。对 \(2^{32}\) 取模这件事情,直接开 unsigned int 自然溢出就行了。时间复杂度 \(O(2^{3m}\log n)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define ui unsigned int
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=(1<<6)+5;
int n,m,p,q,a[5][10];
int sta[maxn],stn;
struct juz{
	ui mat[maxn][maxn];
	juz(){
		for(int i=0;i<1<<m;i++){
			for(int j=0;j<1<<m;j++){
				mat[i][j]=0;
			}
		}
	}
	il ui*operator[](const int &x){
		return mat[x];
	}
	il juz operator*(juz x)const{
		juz res;
		for(int i=0;i<1<<m;i++){
			for(int j=0;j<1<<m;j++){
				for(int k=0;k<1<<m;k++){
					res[i][j]+=mat[i][k]*x[k][j];
				}
			}
		}
		return res;
	}
}dp,bas,fnl;
il juz qpow(juz x,int y){
	juz res;
	for(int i=0;i<1<<m;i++){
		res[i][i]=1;
	}
	while(y){
		if(y&1){
			res=res*x;
		}
		x=x*x,y>>=1;
	}
	return res;
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n)read(m)read(p)read(q);
	q++;
	for(int i=1;i<=3;i++){
		for(int j=1;j<=p;j++){
			read(a[i][j]);
		}
	}
	for(int S=0;S<1<<m;S++){
		for(int i=1;i<=m;i++){
			if(S>>(i-1)&1){
				for(int j=1,k;j<=p;j++){
					if(j==q||!a[2][j]){
						continue;
					}
					k=i-q+j;
					if(k>=1&&k<=m&&(S>>(k-1)&1)){
						goto togo1;
					}
				}
			}
		}
		sta[++stn]=S;
		dp[0][S]=1;
		togo1:;
	}
//	cout<<stn<<"\n";
//	for(int i=1;i<=stn;i++){
//		cout<<bitset<6>(sta[i])<<"\n";
//	}
	for(int x=1;x<=stn;x++){
		for(int y=1;y<=stn;y++){
			for(int i=1;i<=m;i++){
				if(sta[x]>>(i-1)&1){
					for(int j=1,k;j<=p;j++){
						if(!a[3][j]){
							continue;
						}
						k=i-q+j;
						if(k>=1&&k<=m&&(sta[y]>>(k-1)&1)){
							goto togo2;
						}
					}
				}
				if(sta[y]>>(i-1)&1){
					for(int j=1,k;j<=p;j++){
						if(!a[1][j]){
							continue;
						}
						k=i-q+j;
						if(k>=1&&k<=m&&(sta[x]>>(k-1)&1)){
							goto togo2;
						}
					}
				}
			}
			bas[sta[x]][sta[y]]=1;
			togo2:;
		}
	}
	for(int S=0;S<1<<m;S++){
		fnl[S][0]=1;
	}
	printf("%u",(dp*qpow(bas,n-1)*fnl)[0][0]);
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2024-12-21 17:18  zhangxy__hp  阅读(91)  评论(0)    收藏  举报