【模拟赛】排队(动态规划)

题面

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

我们令 d ( i ) d(i) d(i) i i i 位置前比 a i a_i ai 大的数个数,我们想要的就是 ∑ i = 1 n d ( i ) \sum_{i=1}^n d(i) i=1nd(i)

然而我们只能得到 ∑ i = 1 n max ⁡ ( i − a i , 0 ) \sum_{i=1}^n \max(i-a_i,0) i=1nmax(iai,0)

但是不难发现 d ( i ) ≥ max ⁡ ( i − a i , 0 ) d(i)\geq \max(i-a_i,0) d(i)max(iai,0)

所以我们要让两个求和相等,就必须让每个位置的 d ( i ) = max ⁡ ( i − a i , 0 ) d(i)=\max(i-a_i,0) d(i)=max(iai,0) 。把这个条件翻译一下,就是说,每个数前面要么不存在比它大的数,要么存在所有比它小的数

我们可以考虑从左往右向排列中填数,那么每次只有两种选择,其一是填入比最大值大的一个数,其二是填入 前缀集合 ∪ { 0 } \cup\{0\} {0} m e x \rm mex mex ,可以利用这个方法判断前 M M M 个是否合法。令 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示填了第 i i i 个数,最大值为 j j j 的方案数,那么
d p [ i ] [ j ] = 0      ( j < i ) d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + ∑ k < j d p [ i − 1 ] [ k ] dp[i][j]=0~~~~(j<i)\\ dp[i][j]=dp[i-1][j]+\sum_{k<j}dp[i-1][k] dp[i][j]=0    (j<i)dp[i][j]=dp[i1][j]+k<jdp[i1][k]

这样其实不好做,我们令 s u m [ i ] [ j ] = ∑ k = 1 j d p [ i ] [ k ] sum[i][j]=\sum_{k=1}^j dp[i][k] sum[i][j]=k=1jdp[i][k] ,那么
s u m [ i ] [ j ] = s u m [ i ] [ j − 1 ] + ( s u m [ i − 1 ] [ j ] − s u m [ i − 1 ] [ j − 1 ] ) + s u m [ i − 1 ] [ j − 1 ] = s u m [ i ] [ j − 1 ] + s u m [ i − 1 ] [ j ] sum[i][j]=sum[i][j-1]+(sum[i-1][j]-sum[i-1][j-1])+sum[i-1][j-1]\\ =sum[i][j-1]+sum[i-1][j] sum[i][j]=sum[i][j1]+(sum[i1][j]sum[i1][j1])+sum[i1][j1]=sum[i][j1]+sum[i1][j]

最后这个式子非常美观,以至于很容易想到网格图上行走。
s u m [ x ] [ y ] → { s u m [ x + 1 ] [ y ] s u m [ x ] [ y + 1 ] sum[x][y]\rightarrow \begin{cases} sum[x+1][y]\\ sum[x][y+1] \end{cases} sum[x][y]{sum[x+1][y]sum[x][y+1]

我们把前 M M M 个判断后,可以得到最大值 m x mx mx ,那么最终的答案就是从 ( M , m x ) (M,mx) (M,mx) 右走或上走走到 ( N , N ) (N,N) (N,N) ,中途不越过 x = y x=y x=y (保持 y ≥ x y\geq x yx)的方案数。用两个组合数就好了:
C ( 2 N − M − m x , N − M ) − C ( 2 N − M − m x , N − M + 1 ) C(2N-M-mx,N-M)-C(2N-M-mx,N-M+1) C(2NMmx,NM)C(2NMmx,NM+1)

时间复杂度 O ( 对 1 0 5 的 嘲 讽 ) O(对 10^5 的嘲讽) O(105)

CODE

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

const int MOD = 1000000007;
int n,m,s,o,k;
int fac[MAXN<<1],inv[MAXN<<1],invf[MAXN<<1];
int C(int n,int m) {
	if(m < 0 || m > n) return 0;
	return fac[n] *1ll* invf[n-m] % MOD * invf[m] % MOD;
}
int a[MAXN];
int c[MAXN];
void addc(int x,int y) {while(x<=n)c[x]+=y,x+=lowbit(x);}
int sum(int x) {int s=0;while(x)s+=c[x],x-=lowbit(x);return s;}
int main() {
	freopen("queue.in","r",stdin);
	freopen("queue.out","w",stdout);
	n = read();m = read();
	fac[0]=fac[1]=inv[0]=inv[1]=invf[0]=invf[1]=1;
	for(int i = 2;i <= n*2;i ++) {
		fac[i] = fac[i-1] *1ll* i % MOD;
		inv[i] = (MOD - inv[MOD%i]) *1ll* (MOD/i) % MOD;
		invf[i] = invf[i-1] *1ll* inv[i] % MOD;
	}
	int mx = 0,flag = 1;
	for(int i = 1;i <= m;i ++) {
		a[i] = read();
		mx = max(mx,a[i]);
		addc(a[i],1);
		if(a[i] != mx) {
			if(sum(a[i]) != a[i]) flag = 0;
		}
	}
	if(!flag) return printf("0\n"),0;
	int rr = n-m,cc = n-mx;
	int ans = (C(rr+cc,rr) +MOD- C(rr+cc,rr+1)) % MOD;
	AIput(ans,'\n');
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值