AtCoder Regular Contest 164 D.1D Coulomb(dp)

题目

原题意描述比较复杂,但是可以被替换成一个等价的题:

一个由?、+、-构成的长度为2n(n<=3000)的串,?可以填+或-,

合理地填写?,使得串中恰有n个+,n个-

求所有的合法填写方案下,+-均贪心匹配时,所有对+-之间的距离和

答案对998244353取模

思路来源

赛中AC

题解

感觉维护一个方案数,维护一个总和已经是典中典中典了

dp[i][j]表示考虑前i个字母,+比-多j个的方案数,

sum[i][j]表示前i个字母,+比-多j个时,所有方案的距离和

可以发现,每经过一个字母,还有j个字母没匹配时,

这里的没匹配,既可以是+比-多j个,也可以是+比-少j个,

对应的方案数为dp[i][j],对总距离和的贡献为dp[i][j]*j

如果是合法栈匹配,只转移正的就可以了,

但是本题中,正负的转移都是合法的

因为有负值,所以要将下标偏移一下,

按每一位字母,三种情况,转移即可

代码

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
std::mt19937_64 gen(std::chrono::system_clock::now().time_since_epoch().count());
ll get(ll l, ll r) { std::uniform_int_distribution<ll> dist(l, r); return dist(gen); }
const int N=3e3+10,M=2*N,mod=998244353;
int n,dp[M][M],sum[M][M];
char s[M];
void add(int &x,int y){
	x=(x+y)%mod;
}
int main(){
	sci(n);
	scanf("%s",s+1);
	dp[0][N]=1;
	int m=n*2;
	rep(i,1,m){
		if(s[i]=='+'){
			rep(j,N-i,N+i){
				add(dp[i][j],dp[i-1][j-1]);
				add(sum[i][j],sum[i-1][j-1]);
			}
		}
		else if(s[i]=='-'){
			rep(j,N-i,N+i){
				add(dp[i][j],dp[i-1][j+1]);
				add(sum[i][j],sum[i-1][j+1]);
			}
		} 
		else{
			rep(j,N-i,N+i){
				add(dp[i][j],(dp[i-1][j+1]+dp[i-1][j-1])%mod);
				add(sum[i][j],(sum[i-1][j+1]+sum[i-1][j-1])%mod);
			}
		}
		rep(j,N-i,N+i){
			add(sum[i][j],1ll*dp[i][j]*abs(j-N)%mod);
		}
		// rep(j,0,i){
		// 	printf("i:%d j:%d dp:%d sum:%d\n",i,j,dp[i][j],sum[i][j]);
		// }
	}
	pt(sum[m][N]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值