首页 > 其他分享 >一些dp题

一些dp题

时间:2024-10-30 13:08:44浏览次数:1  
标签:std ch int long 一些 dp define

P3736 [HAOI2016] 字符合并

有一个长度为 \(n\) 的 \(01\) 串,你可以每次将相邻的 \(k\) 个字符合并,得到一个新的字符并获得一定分数。

得到的新字符和分数由这 \(k\) 个字符确定。你需要求出你能获得的最大分数。

  • \(1≤n≤300\),\(1<k≤8\)。

  • \(c_i∈{0,1}\),\(1≤w_i≤10^9\)。

对于最大分数,则尽量合并更多的字符,考虑\(k\le8\)可以对\(k\)状压,设计\(f_{l,r,t}\)为区间\([l,r]\)中最终被合并的状态是\(t\)的最大分数,易得这些区间不会相交,则枚举中间的断点\(mid\in [l,r]\),打表很明显能够发现当串的长度为\(a(k-1)+1\)时该串最后生成串的长度一定为\(1\),则当串的长度为\(len\)时,我们考虑\(S\)的最后一位是怎么来的,最后该串的长度为\((len-1) \mod {(k-1)}+1\)

则转移如下:

  • \(dp[i][j][S]= dp[i][mid][S>>1] + dp[mid+1][j][S\&1]\)

  • 对于长度为\(1\)的区间单独讨论

c++

#include"bits/stdc++.h"
using namespace std;
const int N = 310,K = 8;
#define inl inline
#define reg register
#define regi register int
#define PII pair<int,int>
#define ll long long
const int INF = 1e9 + 10;
ll n,k,a[N],c[1 << K],w[1 << K],f[N][N][1 << K];
inl ll read(void)
{
	ll x = 0,f = 1;char ch = getchar();
	while(!isdigit(ch))  f = ch == '-' ? - 1 : f,ch = getchar();
	while(isdigit(ch))   x = (x << 3) + (x << 1) + ch - '0',ch = getchar();
	return x * f;
}
int main(void)
{
	n = read(),k = read();
	for(regi i = 1;i <= n;i ++)	a[i] = read();
	for(regi i = 0;i < (1 << k);i ++)	c[i] = read(),w[i] = read();
	for (regi i = 1;i <= n;i ++)
		for (regi j = 1;j <= n;j ++)
			for (regi z = 0;z < (1 << k);z ++)
				  f[i][j][z] = - INF;
	for(regi i = n;i >= 1;i --)
	{
		for(regi j = i;j <= n;j ++)
		{
			if(i == j)
			{
				f[i][j][a[i]] = 0;
				continue;
			}
			ll len = j - i;
			len %= k - 1;
			if(!len)	len = k - 1;
			for(regi mid = j;mid > i;mid -= k - 1)
			{
				for(regi S = 0;S < (1 << len);S ++)
				{
					f[i][j][S << 1] = max(f[i][j][S << 1],f[i][mid - 1][S] + f[mid][j][0]);
					f[i][j][S << 1 | 1] = max(f[i][j][S << 1 | 1],f[i][mid - 1][S] + f[mid][j][1]);
				}
			}
			if(len == k - 1)
			{
				ll g[2];
				g[0] = g[1] = - INF;
				for(regi S = 0;S < (1 << k);S ++)	g[c[S]] = max(g[c[S]],f[i][j][S] + w[S]);
				f[i][j][0] = g[0],f[i][j][1] = g[1];
			}
		}
	}
	ll ans = - INF;
	for(regi i = 0;i < (1 << k);i ++)	ans = max(ans,f[1][n][i]);
	printf("%lld",ans);
	return 0;
}

----

P5336 [THUSC2016] 成绩单

就是给出一个序列 \(a[1...n]\) 还有\(A\)和\(B\)

每次从序列里抽出连续的一段,这一段的代价为

\(A+B∗(max−min)^2\)

\(n≤50,a≤1500,b≤10,w_i≤1000。\)

设 \(f_{l,r}\)为区间\([l,r]\)成绩单全部被发放所需要的最小代价,\(g_{l,r,mn,mx}\)为区间\([l,r]\)最小值\(mn\)最大值\(mx\)最小代价
易得 \(f_{l,r}=min{\{g_{l,r,mn,mx}+a+b\times(mx-mn)^2\}}\)

1.保留第\(r+1\)张成绩单不取走,未来与 \([l,r]\) 中的成绩单共同取走: \(g_{l,r+1,min(mn,w[r+1]),max(mx.w[r+1])}=min(g_{l,r,mn,mx})\)
- 2.枚举 \(k\),将 \([r+1,k]\) 这段区间的成绩单共同取走

\(g_{l,k,mn,mx}=min(g_{l,r,mn,mx}+f_{r+1,k})\)

可以转化为:

\(g_{l,r,mn,mx}=min(g_{l,k,mn,mx}+f_{k+1,r})\)

c++

#include"bits/stdc++.h"
using namespace std;
const int N = 60;
#define inl inline
#define reg register
#define regi register int
#define PII pair<int,int>
//#define ll long long
int n,a,b,w[N],f[N][N],g[N][N][N][N],d[N];
int tot;
inl int read(void)
{
	int x = 0,f = 1;char ch = getchar();
	while(!isdigit(ch))  f = ch == '-' ? - 1 : f,ch = getchar();
	while(isdigit(ch))   x = (x << 3) + (x << 1) + ch - '0',ch = getchar();
	return x * f;
}
int main(void)
{
	n = read(),a = read(),b = read();
	for(regi i = 1;i <= n;i ++)	w[i] = read(),d[i] = w[i];
	sort(d + 1, d + n + 1);tot = unique(d + 1,d + n + 1) - d - 1;
	for(regi i = 1;i <= n;i ++)	w[i] = lower_bound(d + 1,d + tot + 1,w[i]) - d,f[i][i] = a;
	memset(g,0x3f,sizeof g);memset(f,0x3f,sizeof f);
	for(regi i = 1;i <= n;i ++)	g[i][i][w[i]][w[i]] = 0;
	for(regi len = 1;len <= n;len ++)
		for(regi l = 1,r = len;r <= n;l ++,r ++)
			for(regi mn = 1;mn <= tot;mn ++)
			{
				for(regi mx = mn;mx <= tot;mx ++)
				{
					for(regi k = l;k < r;k ++)	g[l][r][mn][mx] = min(g[l][k][mn][mx] + f[k + 1][r],g[l][r][mn][mx]);
					g[l][r + 1][min(mn,w[r + 1])][max(mx,w[r + 1])] = min(g[l][r + 1][min(mn,w[r + 1])][max(mx,w[r + 1])],g[l][r][mn][mx]);
					f[l][r] = min(f[l][r],g[l][r][mn][mx] + a + b * (d[mx] - d[mn]) * (d[mx] - d[mn]));
				}
			}
	printf("%d",f[1][n]);
	return 0;
}
 
----

P3349 [ZJOI2016] 小星星

抽象化题意:

一个\(n\)个节点的树,和一个\(n\)个节点的图,要求给树上的每个节点编号,使得编号是一个\(1\)到\(n\)的排列,并且要满足树上任意一条边\((u,v)\),图中一定要有边\((x_u,x_v)\)(\(x_u\)表示点\(u\)的编号),求方案数。

\(n≤17\),\(m≤\frac{1}{2}n(n-1)\)。

考虑暴力:设\(f_{i,j,S}\)为节点\(i\)编号为\(j\),\(i\)的子树内的编号集合为\(S\)的方案数。这样转移时间复杂度为\(O(n^3\times3^n)\)肯定过不去

考虑去掉\(S\)这一维\(f_{i,j}\)为节点\(i\)编号为\(j\)的方案数

如果出现重复的编号,考虑容斥

\(2^n\)枚举\(\{1,2,3...n\}\)的一个子集\(S\),显然容斥系数是\((-1)^{n-|S|}\)

c++

#include"bits/stdc++.h"
using namespace std;
const int N = 20;
#define inl inline
#define reg register
#define regi register int
#define PII pair<int,int>
#define ll long long
ll n,m,tot,f[N],exi[N];
ll dp[N][N],ans;
vector	ver[N];
struct node
{
	ll v,net;
}edge[N << 1];
inl ll read(void)
{
	ll x = 0,f = 1;char ch = getchar();
	while(!isdigit(ch))  f = ch == '-' ? - 1 : f,ch = getchar();
	while(isdigit(ch))   x = (x << 3) + (x << 1) + ch - '0',ch = getchar();
	return x * f;
}
inl void dfs(int u,int fa)
{
	for(regi i = 1;i <= n;i ++)	dp[u][i] = 1;
	for(regi i = f[u];i;i = edge[i].net)
	{
		int v = edge[i].v;
		if(v == fa) continue;
		dfs(v,u);
		for(regi j = 1;j <= n;j ++)
		{
			ll s = 0;
			for(regi l = 0;l < ver[j].size();l ++)
			{
				int k = ver[j][l];
				s += dp[v][k] * (exi[j] & exi[k]);
			}
			dp[u][j] *= s;
		}
	}
}
int main(void)
{
	n = read(),m = read();
	for(regi i = 1;i <= m;i ++)
	{
		int u = read(),v = read();
		ver[u].push_back(v);
		ver[v].push_back(u);
	}
	for(regi i = 2;i <= n;i ++)
	{
		int u = read(),v = read();
		edge[++ tot] = node{v,f[u]},f[u] = tot;
		edge[++ tot] = node{u,f[v]},f[v] = tot;
	}
	for(regi i = 0;i < (1 << n);i ++)
	{
		int x = n;
		memset(dp,0,sizeof dp);
		memset(exi,0,sizeof exi);
		for(regi j = 1;j <= n;j ++)	if(i & (1 << j - 1))	exi[j] = 1,x --;
		dfs(1,0);
		for(regi j = 1;j <= n;j ++)	ans += dp[1][j] * (x % 2 ? - 1 : 1);
	}
	printf("%lld",ans);
	return 0;
}
 
-----

P4516 [JSOI2018] 潜入行动

给一棵 \(n\) 个点的树,试选取 \(k\) 个点,使得树中的每一个点都至少与该 \(k\) 个点中的一个点直接相连。( \(k\) 个点中的点不与自身相连 )

\(n≤10^5,k≤100\)

很明显,要跑背包

\(dp[x][i][0/1][0/1]\)表示以\(x\)为根的子树中共放了\(i\)个监听装置,其中\(x\)点放没放装置,\(x\)点有没有被监听到的方案数

\(dp[x][i+j][0][0]=∑dp[x][i][0][0]∗dp[v][j][0][1]\)
\(dp[x][i+j][1][0]=∑dp[x][i][1][0]∗(dp[v][j][0][0]+dp[v][j][0][1])\)
\(dp[x][i+j][0][1]=∑(dp[x][i][0][1]∗(dp[v][j][0][1]+dp[v][j][1][1])+dp[x][i][0][0]∗dp[v][j][1][1])\)
\(dp[x][i+j][1][1]=∑(dp[x][i][1][0]∗(dp[v][j][1][0]+dp[v][j][1][1])+dp[x][i][1][1]∗(dp[v][j][0][0]+dp[v][j][0][1]+dp[v][j][1][0]+dp[v][j][1][1]))\)

c++

#include"bits/stdc++.h"
using namespace std;
#define inl inline
#define reg register
#define regi register int
#define ll long long
#define PII pair<int,int>
const int N=100015,P=1e9+7,M=101;
inl int read(void)
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch))  f=ch=='-'?-1:f,ch=getchar();
	while(isdigit(ch))   x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*f;
}
int n,K;
int f[N][M][2][2],tmp[M][2][2];
int sz[N];
int ver[N<<1],net[N<<1],cnt,head[N];
void addedge(int u,int v){ver[++cnt]=v,net[cnt]=head[u],head[u]=cnt;}
void dfs(int u,int fa)
{
	sz[u]=1,f[u][0][0][0]=f[u][1][1][0]=1;
	for(int i=head[u];i;i=net[i])
	{
		int v=ver[i];
		if(v==fa)	continue;
		dfs(v,u);
		for(int j=0;j<=sz[u]&&j<=K;j++)
			for(int k=0;k<=sz[v]&&j+k<=K;k++)
			{
				if(f[u][j][0][1])
					(tmp[j+k][0][1]+=1ll*f[u][j][0][1]*(f[v][k][0][1]+f[v][k][1][1])%P)%=P;
				if(f[u][j][1][0])
					(tmp[j+k][1][0]+=1ll*f[u][j][1][0]*(f[v][k][0][0]+f[v][k][0][1])%P)%=P,
					(tmp[j+k][1][1]+=1ll*f[u][j][1][0]*(f[v][k][1][0]+f[v][k][1][1])%P)%=P;
				if(f[u][j][0][0])
					(tmp[j+k][0][0]+=1ll*f[u][j][0][0]*f[v][k][0][1]%P)%=P,
					(tmp[j+k][0][1]+=1ll*f[u][j][0][0]*f[v][k][1][1]%P)%=P;
				if(f[u][j][1][1])
				{
					int res=0;
					(res+=f[v][k][0][0])%=P,(res+=f[v][k][0][1])%=P,
					(res+=f[v][k][1][0])%=P,(res+=f[v][k][1][1])%=P,
					(tmp[j+k][1][1]+=1ll*f[u][j][1][1]*res%P)%=P;
				}
			}
		sz[u]+=sz[v];
		for(int j=0;j<=sz[u]&&j<=K;j++)
			f[u][j][0][0]=tmp[j][0][0],tmp[j][0][0]=0,
			f[u][j][0][1]=tmp[j][0][1],tmp[j][0][1]=0,
			f[u][j][1][0]=tmp[j][1][0],tmp[j][1][0]=0,
			f[u][j][1][1]=tmp[j][1][1],tmp[j][1][1]=0;
	}
}
signed main(void)
{
	n=read(),K=read();
	for(int i=1,u,v;i<=n-1;i++) u=read(),v=read(),addedge(u,v),addedge(v,u);
	dfs(1,0);
	int ans=(f[1][K][0][1]+f[1][K][1][1])%P;
	printf("%d",ans);
	return 0;
}
 
----

CF1442D Sum

给定 \(n\) 个不降的数组。有一个值 \(ans\),初始为 \(0\)。你需要进行如下操作 \(k\) 次:

  • 选择一个数组,把 \(ans\) 加上数组的第一个元素,之后把它删除。

请求出 \(ans\) 最大是多少。

所有数组的元素总个数 \(\leq 10^6\),\(n,k\leq 3000\)。

对于所有序列,显然选部分的序列个数 \(≤1\),其它全选或全不选。

有了上述结论,我们只要枚举哪个选部分,其余数列当做 \(01\) 背包问题即可。

  • 对于区间 \([l,r]\),找到一个中间点 \(mid\)。

  • 先将 \([l,mid]\) 中的物品插入到背包中,然后 \(solve(mid+1,r)\)。

  • 将背包还原,再将 \([mid+1,r]\) 中的物品插入到背包中,然后 \(solve(l,mid)\)。

  • 这样一来,如果当前的区间是 \([l,r]\),可以发现此时背包中的物品恰为 \([1,l−1]∪[r+1,n]\) 中的物品!

  • 因此,当 \(l=r\) 时我们得到的就是想要的答案。

时间复杂度为\(O(knlogn)\)轻松过

c++

#include"bits/stdc++.h"
using namespace std;
const int N = 3010;
#define inl inline
#define reg register
#define regi register int
#define PII pair<int,int>
#define int long long
using namespace std;
inl int read(void)
{
	int x = 0,f = 1;char ch = getchar();
	while(!isdigit(ch))  f = ch == '-' ? - 1 : f,ch = getchar();
	while(isdigit(ch))   x = (x << 3) + (x << 1) + ch - '0',ch = getchar();
	return x * f;
}
int n,k,x,t[N],sum[N],ans,dp[N];
vector a[N];
void solve(int l,int r)
{
    int mid = (l + r) >> 1;
    if(l == r)
	{
        int now = 0;
        for(int i = 0;i <= min(t[l],k);i ++)
		{
            ans = max(ans,now + dp[k - i]);
            now += a[l][i];
        }
        return;
	}
    for(regi i = mid + 1;i <= r;i ++)
        for(regi j = k;j >= t[i];j --)
            dp[j] = max(dp[j],dp[j - t[i]] + sum[i]);
    solve(l,mid);
    for(regi i = 0;i <= k;i ++)	dp[i] = f[i];
    for(regi i = l;i <= mid;i ++)
        for(regi j = k;j >= t[i];j --)
            dp[j] = max(dp[j],dp[j - t[i]] + sum[i]);
    solve(mid + 1,r);
}
signed main(void)
{
    n = read(),k = read();
    for(regi i = 1;i <= n;i ++)
    {
        t[i] = read();
        for(regi j = 1,tmp;j <= t[i];j ++)
    	{
            tmp = read();
            a[i].push_back(tmp);
            sum[i] += tmp;
        }
    }
    solve(1,n);
    printf("%lld",ans);
    return 0;
}
   
------

P6773 [NOI2020] 命运

题意

给定一棵 \(n\) 个点的树和 \(m\) 条限制,你可以给树上的每一条边赋一个 \(0\) 或 \(1\) 的权值。对于所有限制 \((u,v)\)(保证 \(v\) 为 \(u\) 的祖先) 你需要保证 \(u\) 到 \(v\) 上至少有一条边的权值为 \(1\),求赋值方案数。

Data Range:\(1≤n,m≤5×10^5\)

设\(f_{i,j}\)表示\(i\)往上第一条权值为\(1\)的边的深度为\(j\)时,子树的方案数,那么对于每一条限制\((x=>y)\),相当于令\(y\)的\(f_{y,p}=0(p\in[dep_x,n])\),不妨记\(lim_i\)表示\(i\)往上最远的权值为\(1\)的边至多深度为多浅

转移时,考虑自己与儿子之间的边为\(1\)还是\(0\),可以得到:

\[f_{i,j}=\prod_{y \in son}{f_{y,j}+f_{y,dep_y}} \]

用线段树合并来优化,在计算贡献之前令所有儿子的\(f_{y,j}\)加上\(f_{y,dep_y}\),贡献时恰好是让自己的第\(j\)位贡献父亲的第\(j\)位,这完全是对应的,用线段树维护第二维,维护区间加和区间乘

c++

#include"bits/stdc++.h"
using namespace std;
const int N=5e5+15,P=998244353;
#define inl inline
#define reg register
#define regi register int
#define PII pair<int,int>
#define ll long long
#define int long long
inl int read(void)
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch))  f=ch=='-'?-1:f,ch=getchar();
	while(isdigit(ch))   x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*f;
}
vector v[N];
int n,m;
void mod(ll &x){x%=P;}
struct node
{
	int ls,rs;
	int s,mul;
	#define mul(p)	tr[p].mul
	#define ls(p)	tr[p].ls
	#define rs(p)	tr[p].rs
	#define s(p)	tr[p].s
}tr[N<<6];
void pushdown(int p)
{
	if(ls(p))
		s(ls(p))=s(ls(p))*mul(p)%P,mul(ls(p))=mul(ls(p))*mul(p)%P;
	if(rs(p))
		s(rs(p))=s(rs(p))*mul(p)%P,mul(rs(p))=mul(rs(p))*mul(p)%P;
	mul(p)=1;
}
int head[N],ver[N<<1],net[N<<1],cnt,dep[N];
void addedge(int a,int b)
{
	ver[++cnt]=b,net[cnt]=head[a],head[a]=cnt;
}
ll query(int p,int l,int r,int x)
{
	if(!p||r<=x)	return s(p);
	int mid=l+r>>1;ll s=0;
	pushdown(p);
	if(mid<=x-1)	mod(s+=query(rs(p),mid+1,r,x));
	mod(s+=query(ls(p),l,mid,x));
	return s;
}
int tot;
void change(int &p,int l,int r,int x)
{
	p=++tot,s(p)=mul(p)=1;
	if(l==r)	return ;
	int mid=l+r>>1;
	if(x<=mid)	change(ls(p),l,mid,x);
	else	change(rs(p),mid+1,r,x);
}
int merge(int x,int y,int l,int r,ll &s1,ll &s2)
{
	if(!x&&!y)	return 0;
	if(!x||!y)
	{
		if(!x)
		{
			mod(s1+=s(y));
			mul(y)=mul(y)*s2%P,s(y)=s(y)*s2%P;
			return y;
		}
		mod(s2+=s(x));
		s(x)=s(x)*s1%P,mul(x)=mul(x)*s1%P;
		return x;
	}
	if(l==r)
	{
		ll tx=s(x),ty=s(y);mod(s1+=ty);
		s(x)=(s(x)*s1+s(y)*s2)%P;
		mod(s2+=tx);
		return x;
	}
	pushdown(x),pushdown(y);
	int mid=l+r>>1;
	ls(x)=merge(ls(x),ls(y),l,mid,s1,s2);
	rs(x)=merge(rs(x),rs(y),mid+1,r,s1,s2);
	s(x)=(s(ls(x))+s(rs(x)))%P;
	return x;
}
int boring[N];
void dfs(int x,int fa)
{
	dep[x]=dep[fa]+1;int res=0;
	for(auto t:v[x])	res=max(res,dep[t]);
	change(boring[x],0,n,res);
	for(int i=head[x];i;i=net[i])
	{
		int y=ver[i];
		if(y==fa)	continue;
		dfs(y,x);
		ll S=query(boring[y],0,n,dep[x]),S_=0;
		boring[x]=merge(boring[x],boring[y],0,n,S,S_);
	}
}
signed main(void)
{
	n=read();
	for(int i=1,u,v;i<=n-1;i++)
		u=read(),v=read(),addedge(u,v),addedge(v,u);
	m=read();
	for(int i=1,x,y;i<=m;i++)
		x=read(),y=read(),v[y].push_back(x);
	dfs(1,0);
	printf("%lld",query(boring[1],0,n,0));
	return 0;
}
   

P4590 [TJOI2018] 游园会

题目大意

令 \(f(i)\) 为满足下面几条限制的字符串个数:

  • 长度为 \(n\),字符集为 \({N,O,I}\)。
  • 不包含子串 \(NOI\)。
  • 与模式串 \(s\) 的最长公共子序列\((lcs)\)长度为 \(i\)。

对于 \(i∈[0,k]\),求出 \(f_i\) 的值,对 \(10^9+7\) 取模。

\(DP\) 套 \(DP\) 板子题

所谓 \(dp\) 套 \(dp\) ,实际上就是在说求解一个 \(dp\) 的过程中,我们用另一个 \(dp\) 求解出他应该从某个状态转移到另一个状态。

设求 \(LCS\) 的 \(dp:\) \(dp_{i,j}=max{\{dp_{i-1,j},dp_{i,j-1},dp_{i-1,j-1}+[s_i=t_i]\}}\)

显然当\(i\)一定时,\(dp_{i,j}\)时单调不降的,且相邻两个数的差\(\in[0,1]\),通过状压把 \(LCS\) 数组的状态设到转移方程里

设\(f_{i,S,0/1/2}\)表示当考虑了串的前\(i\)位的时候,\(LCS\)的\(dp\)式子的状态为\(S\),最后几位同"\(NOI\)"的匹配到了第\(k\)位\((k\in[0,2])\),此时的方案数

设\(sta_{S,j}\)表示\(LCS\)的\(dp\)式子在状态为\(S\)时新加入一位\(j\)后的\(dp\)式子状压下来的情况,设\(nxt_{x,y}\)表示下一个"\(NOI\)"匹配的状态,初始状态\(f_{0,0,0}=1\),

\[f_{i,sta_{S,x},nxt_{y,x}}\to f_{i,sta_{S,x},nxt_{y,x}}+f_{i-1,S,y} \]

时间复杂度为\(O(n2^k+k2^k)\)

c++

#include"bits/stdc++.h"
using namespace std;
const int N = 1010,mod = 1e9 + 7;
#define inl inline
#define reg register
#define regi register int
#define PII pair<int,int>
//#define ll long long
inl int read(void)
{
	int x = 0,f = 1;char ch = getchar();
	while(!isdigit(ch))  f = ch == '-' ? - 1 : f,ch = getchar();
	while(isdigit(ch))   x = (x << 3) + (x << 1) + ch - '0',ch = getchar();
	return x * f;
}
int n,m,f[2][N],a[N],b[N];
int nxt[3][3]={{1,0,0},{1,2,0},{1,0,3}};
int dp[5][100010][5],sta[100010][5];
int ans[N],now;
char ch[N];
inl void init()
{
	for(int p = 0;p < (1 << m);p ++)
	{
		for(int k = 0;k < m;k ++)	f[0][k + 1] = f[0][k] + ((p >> k) & 1);
		for(int j = 0;j <= 2;j ++)
		{
			for(int k = 1;k <= m;k ++)
			{
				f[1][k] = max(f[1][k - 1],f[0][k]);
				if(b[k] == j)	f[1][k] = max(f[1][k],f[0][k - 1] + 1);
			}
			int t = 0;
			for(int i = 1;i <= m;i ++)	if(f[1][i] > f[1][i - 1])	t |= (1 << (i - 1));
			sta[p][j] = t;
		}
	}	
}
int main(void)
{
	n = read(),m = read();
	cin >> (ch + 1);
	for(int i = 1;i <= m;i ++)
	{
		if(ch[i] == 'N')	b[i]=0;
		if(ch[i] == 'O')	b[i]=1;
		if(ch[i] == 'I')	b[i]=2;
	}
	init();
	dp[0][0][0] = 1;
	for(regi i = 1;i <= n;i ++)
	{
		memset(dp[now ^ 1],0,sizeof(dp[now ^ 1]));
		for(regi p = 0;p < (1 << m);p ++)
		{
			for(regi j = 0;j <= 2;j ++)
			{
				for(regi k = 0;k <= 2;k ++)
				{
					if(j == 2 && k == 2)	continue;
					int fa = nxt[j][k];
					dp[now ^ 1][sta[p][k]][fa] += dp[now][p][j];
					dp[now ^ 1][sta[p][k]][fa] %= mod;
				}
			} 
		}
		now ^= 1;
	}
	for(regi p = 0;p < (1 << m);p ++)
	{
		for(regi j = 0;j <= 2;j ++)
		{
			int fa = 0,le = p;
			while(le){
				if(le %2 == 1)	fa ++;
				le /= 2;
			}
			ans[fa] += dp[now][p][j];
			ans[fa] %= mod;
		}
	}
	for(regi i = 0;i <= m;i++)	printf("%d\n",ans[i]);
	return 0; 
}
   

标签:std,ch,int,long,一些,dp,define
From: https://www.cnblogs.com/empty-space/p/18515658

相关文章

  • Atcoder DP Contest
    好像都在说这套题很好,所以来刷一遍太水的就只放代码了尚未完工,先发一下猫猫可爱捏https://www.tldraw.com/ro/1g8hQBpWTkduIlFxT3c0P?d=v-1275.1523.960.968.pageA.Frog1#include<bits/stdc++.h>usingnamespacestd;intn;inth[100001];intf[100001];intmain(){ ......
  • 01背包问题(经典dp题解)
    有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 ii 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。输入格式第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。接下来......
  • 【linux网络编程】| socket套接字 | 实现UDP协议聊天室
        前言:本节内容将带友友们实现一个UDP协议的聊天室。主要原理是客户端发送数据给服务端。服务端将数据再转发给所有链接服务端的客户端。所以,我们主要就是要实现客户端以及服务端的逻辑代码。那么,接下来开始我们的学习吧。    ps:本节内容建议了解so......
  • [CSP-J 2022] 上升点列(DP)
    题目传送门解题思路首先先讲这些点按照  从小到大排序。然后,很容易想到设  表示到第  个点已经放了  个点的最长上升序列的长度。所以,我们可以从前面的点转移(注意要判断一下 是否符合,因为我们只按照了 排序);于是,手推一下可以整出这样一个转移方程:其中  是......
  • ScheduledThreadPoolExecutor的介绍与使用
    ScheduledThreadPoolExecutor是Java中的一个类,它继承自ThreadPoolExecutor,并实现了ScheduledExecutorService接口。这个类主要用于在给定的延迟之后或周期性地执行任务,是处理定时任务的一个强大工具。一、主要特点线程池大小固定:ScheduledThreadPoolExecutor的线程池大小......
  • STM32+致远电子Dport模块的Ethercat从站开发
    环境准备硬件环境1.Dport-stm32评估板2.stlink3.千兆网线4.安装有twincat3的上位机电脑(带千兆网口) 软件环境1.TC31-FULL-Setup.3.1.4024.53.exe2.mdk5开发环境3.SSCTool.exe4.stm32cubemx 例程资料1.致远电子官网 开发流程1.底层硬件EPC103-DP系统框图,......
  • 总结yolov8做图像实例分割训练时的一些常识点
    计算机视觉中的几个重要的研究方向。主要包括图像分类、目标检测、语义分割、实例分割、全景分割等那么何为实例分割?实例分割比目标检测更进一步,涉及识别图像中的各个对象并将它们与图像的其余部分分割开来。 图像分割可分为:语义分割,实例分割,全景分割。(a)原图,(b)语义分......
  • JS和ECharts的一些分享
    @TJS和ECharts的一些分享OC制作动态天气预测图表withEChartsandJavaScript在现代网页开发中,将复杂的数据视觉化通常能极大地增强用户体验。本文将使用ECharts,一款强大的开源可视化库,配合JavaScript来创建一个根据不同天气条件显示不同的动态液体填充图表。这种图表......
  • 【学习笔记】dp 优化
    单调栈&单调队列没啥好说的。放两道题目。线段树优化dp例题CF115ELinearKingdomRaces容易想到记\(f_{i,j}\)表示前\(i\)个跑道,\([i-j+1,i]\)全部修好的最大利润,但不好优化。考虑转化为表示\([j,i]\)全部修好的最大利润。最简单的状态转移方程:\[f_{i,j}=f_{i-1,......
  • 一些题目
    一些最近刷到的好题,还有一些没见过的处理方式。原题:FunctionQuery定义\(f(x)=(x\oplusa)-b\),其中\(a,b\)是给定的参数。给定\(n\)个变量,\(x_1,x_2,x_3,\dots,x_n\),给出\(q\)组询问,对于每组询问,给定\(a,b\),请你输出一个\(i\),满足\(i\in[1,n)\),且有\(f(x_i)\times......