CF1809G prediction - dp - 组合数学 -

题目链接:https://codeforces.com/contest/1809/problem/G

题解:
一道很强的 dp
首先翻译条件:predictable 是什么意思?发现就是对每一个下标,前缀 max 和下一个位置至少差一个 k+1
看到 n106,可以猜测最后应该是线性的,考虑线性 dp
dp[i] 表示已经选了前 i 大 rating 的人,并且第一个位置填的数不能和剩下没选的人冲突,这里“冲突”指的是 |xy|k
为什么要这么设状态?因为我们不可能再设一维状态来表示前缀 max,因此我们需要确保每一次转移的时候都转移到合法状态
假设现在已经填了前 i 大的数,那么第 i+1 个数有以下两种填法:

  • 填在第 1i 个数的后面,那么显然这个状态一定合法(开头的数没有变),转移为 dp[i+1]dp[i]×i
  • 填在第 1 个数前面(即放在开头),那么和当前数冲突的数字(必然是 i+2to[i]1 连续的一段)应该也一块放,这样保证状态还是合法的。考虑先放第 i+2,有 i 种方法(放在第 1i 的后面),再放 i+3,有 i+1 种……最后再放开头的 i+1,转移为 dp[to[i+1]1]dp[i]×(i××to[i+1]3)

代码:

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 1e6+5, mod = 998244353;

int n,k,a[maxn],dp[maxn];
int fac[maxn], inv[maxn], to[maxn];

int pw(int x,int y){
	if(!y)return 1;
	if(y==1)return x;
	int mid=pw(x,y>>1);
	if(y&1)return 1ll*mid*mid%mod*x%mod;
	return 1ll*mid*mid%mod;
}

int que(int l,int r){
	return 1ll*fac[r]*inv[l-1]%mod;
}

signed main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	reverse(a+1,a+n+1);
	
	for(int i=1, j=1;i<=n;i++){
		while(j <= n && a[i] - a[j] <= k)++ j;
		to[i] = j;
	}
	
	fac[0] = inv[0] = 1;
	for(int i=1;i<=n;i++)fac[i] = 1ll*fac[i-1]*i%mod;
	inv[n] = pw(fac[n], mod-2);
	for(int i=n-1;i>=1;i--)inv[i] = 1ll*inv[i+1]*(i+1)%mod;
	
	if(a[1] - a[2] > k)dp[1] = 1;
	for(int i=1;i<n;i++){
		// (i+2) .. (to[i]-1)
//		if(to[i+1]-1 > i+1)
		(dp[to[i+1]-1] += 1ll*dp[i]*que(i, to[i+1]-3)%mod) %= mod;
		(dp[i+1] += 1ll*dp[i]*i%mod) %= mod;
	}
	printf("%d\n",dp[n]);

	return 0;
}
posted @   SkyRainWind  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示