【Codeforces 1093F】Vasya and Array | 思维、dp、容斥

题目大意:

给出一段序列,序列中有 − 1 -1 1 x ( 1 ≤ x ≤ m ) x(1 \le x \le m) x(1xm) − 1 -1 1可以替换为 [ 1 , m ] [1,m] [1,m]中任何一个数。

认定一个序列是好的,当且仅当序列中所有连续相同数的长度都小于 l e n len len

询问有多少种方案使得该序列是好的。

题目思路:

e m m m emmm emmm,这个 d p dp dp确实有点难想,主要是没做过这种,总结一下。

定义

  • d p [ i ] [ j ] dp[i][j] dp[i][j]代表前 i i i个数合法最后一个数是 j j j的方案数。
  • s p [ i ] = ∑ j = 1 m d p [ i ] [ j ] sp[i] = \sum_{j=1}^mdp[i][j] sp[i]=j=1mdp[i][j]

首先令 d p [ i ] [ j ] = s p [ i − 1 ] dp[i][j] = sp[i-1] dp[i][j]=sp[i1],那么可以肯定的是,这已经包含了所有的情况,即存在非法情况。所以可以考虑将非法的情况减去,如果确定第 i i i位放入 j j j这个数字,当且仅当 [ i − l e n + 1 , i ] [i-len+1,i] [ilen+1,i]全是 − 1 -1 1或者 j j j时,才会对当前产生非法的贡献。

所以首先 d p [ i ] [ j ] = s p [ i − 1 ] − s p [ i − l e n ] dp[i][j] = sp[i-1] - sp[i-len] dp[i][j]=sp[i1]sp[ilen]

这样就把 [ i − l e n + 1 , i ] [i-len+1,i] [ilen+1,i]取同一个数的非法情况减掉了,但是这个多减了一部分,因为有一部分可以被前 i − 1 i-1 i1个数容斥掉,就是 d p [ i − l e n ] [ j ] dp[i-len][j] dp[ilen][j],所以需要将 d p [ i − l e n ] [ j ] dp[i-len][j] dp[ilen][j]补充上。

所以 d p dp dp转移方程:

  • d p [ i ] [ j ] = s p [ i − 1 ] − s p [ i − l e n ] + d p [ i − l e n ] [ j ] dp[i][j] = sp[i-1] - sp[i-len] + dp[i-len][j] dp[i][j]=sp[i1]sp[ilen]+dp[ilen][j] , 满足容斥条件时
  • d p [ i ] [ j ] = s p [ i − 1 ] dp[i][j] = sp[i-1] dp[i][j]=sp[i1], 其他情况

Code:

/*** keep hungry and calm CoolGuang!  ***/
//#pragma GCC optimize(3)
#include 
#include
#include
#include
#include
#include
#define debug(x) cout<<#x<<":"<
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e18+7;
const int maxn = 1e6+700;
const int mod= 998244353;
const double eps = 1e-9;
const double PI = acos(-1);
template<typename T>inline void read(T &a){
     char c=getchar();T x=0,f=1;while(!isdigit(c)){
     if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
     x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
ll dp[100005][105];
int sp[100005];
int sum[100005][105];
int a[100005];
int main(){
     
	read(n);read(m);read(p);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++){
     
		for(int k=1;k<=m+1;k++) sum[i][k] = sum[i-1][k];
		sum[i][(a[i]==-1?m+1:a[i])]++;
	}
	sp[0] = 1;

	for(int i=1;i<=n;i++){
     
		if(a[i] == -1){
     
			for(int k=1;k<=m;k++){
     
				dp[i][k] = sp[i-1];
				if(i<p) continue;
				if(sum[i][k]-sum[i-p][k] + sum[i][m+1]-sum[i-p][m+1] == p)
					dp[i][k] = (dp[i][k] - sp[i-p]%mod+mod + dp[i-p][k])%mod;
			}
		}
		else{
     
			for(int k=a[i];k<=a[i];k++){
     
				dp[i][k] = sp[i-1];
				if(i<p) continue;
				if(sum[i][k]-sum[i-p][k] + sum[i][m+1]-sum[i-p][m+1] == p)
					dp[i][k] = (dp[i][k] - sp[i-p]%mod+mod + dp[i-p][k])%mod;
			}
		}
		for(int k=1;k<=m;k++) sp[i] = (sp[i] + dp[i][k])%mod;
	}
	ll ans = 0;
	for(int i=1;i<=m;i++) ans = (ans + dp[n][i])%mod;
		dl(ans);
  return 0;
}

/**
2 2
-1 -2
4 5
**/

你可能感兴趣的:(其他dp(线性,思维,etc))