#C241116B. 挂分


#C241116B. 挂分

标签(Label)

  • 数学
  • 观察+分析

网址(Website)

题目详情 - 挂分 - Super

题解 - 挂分 - Super

题目(Problem)

样例

输入数据 1

3
1 2 3
2

输出数据 1

4

【样例 1 解释】

可能的挂分情况有 $\{0,0,0\}$,$\{1,0,0\}$,$\{0,0,1\}$,$\{1,0,1\}$ 。

注意 $\{1,0,2\}$ 不是合法的挂分情况,因为第三个人挂了 $2$ 分后,得分低于第二个人。

注意 $\{1,1,1\}$ 不是合法的挂分情况,因为第 $x=2$ 个人不能挂分。

注意 $\{2,2,2\}$ 不是合法的挂分情况,因为第一个人不能挂掉比自己原本更高的分。

输入数据 2

5
1 1 2 3 5
4

输出数据 2

12

【样例 3】

见选手目录下的 fate/fate3.infate/fate3.ans

【样例 4】

见选手目录下的 fate/fate4.infate/fate4.ans

题解(Solution)

44分

考虑 $\mathrm{dp}$ 。设 $f_{i,j}$ 表示当前位置为 $i$ ,当前值取 $j$ 的方案数,设 $A$ 表示值域,很明显左边和右边可以分开做,然后计算最后的答案。

先考虑左边的情况,手推可得, $f_{i,j}=\sum_{k=j}^{j+a_{i+1}-a_i}f_{i+1,k}$ ;对于右边的情况, $f_{i,j}=\sum_{k=j}^{a_{i-1}-a_i+j}f_{i-1,k}$ 。特别的,对于不会挂分的 $p$ 位置,有 $f_{p-1,j}=1\vert j\in[0,a[p]]$ 和 $f_{p+1,j}=1 | j\in[a[p],A]$ 。

最后的答案就是 $\sum_{j=0}^A f_{1,j}\times \sum_{j=0}^A f_{n,j}$ 。时间复杂度 $O(nA)$ 。

注意:实现可能与上面讲的不一样,但是上面讲的要更加简单一些。

52分

从上面的式子可以推得,对于每个转移,我们先考虑左边的情况,对于 $a_i$ 位置还剩 $j$ 分,我们能从 $a_{i+1}$ 转移必须满足他们的 $b_i\le b_{i+1}$ ,又因为 $b_i=a_i-j$ ,令上一个状态还剩 $k$ 分,则有 $a_i-j\le a_{i+1}-k \Rightarrow k\le a_{i+1}-a_i+j$ 又因为升序的限制,则 $k\in[j,j+a_{i+1}-a_i]$ ,由于最开始的时候所有有值的 $f_i$ 都是 $1$ ,我们发现向前推的时候每个点都会进行这样的转移,则有 $f_{i,j} = \sum_{k=j}^{j+a_{i+1}-a_i}f_{i+1,k} = f_{i+1,j}\times(a_{i+1}-a_i+1)$ ,那么左侧的答案其实就是 $(a_1+1)\times\Pi_{i=2}^{p-1} (a_{i+1}-a_i)$ ,结合上面的实现,一共可以拿 $52$ 分。

100分

和 $52$ 分的一种思路,我们通过打出大样例的每一个 $f_{i,j}$ ,惊奇的发现右边好像只有 $a_{p+1}-a_p$ 的部分不为 $0$ ,那么我们就只拿出这 $a_{p+1}-a_p$ 份,并且列成一个表格,惊奇的发现这个表格像是一个斜着的杨辉三角,那么就可以直接用组合数来做这道题了,经过观察得知, $f_{n,j}=\binom{(n-p)+j-1}{j}$ ,由于 $a_i\le 10^8$ ,我们完全可以通过这道题。

注意特判 $p=n$ 的情况。

出题者题解### 算法分析

题目来源:原创。

对于 $n, a_i \leq 9$ 的部分分,可以考虑搜索出所有可能的情况,复杂度 $O(n^n)$。

不难发现,$x$ 左右两边是独立的,因为左边的人分数始终不超过 $a_x$,右边的人分数始终不低于 $a_x$,因此可以考虑将问题拆成 $x$ 左边和 $x$ 右边,然后用乘法原理计算答案。在 $x = \lfloor \frac{1 + n}{2} \rfloor$ 的情况,不难发现搜索的规模可以减半。

我们接下来单独考虑 $x$ 分别在左右侧的情况,也就是 $x = 1$ 和 $x = n$ 的部分分。

考虑 $x = 1$,也就是要求 $x$ 右侧的不降序列,再减去一个不增序列,仍然是一个不降序列,且要求最终序列第一个元素不小于 $a_x$。

不难发现,在这种情况下,如果最终序列第一个元素不小于 $a_x$ 的要求被满足,后面的元素依然还是不降的(后面更大的元素,减去了一个更小的数,显然还是不会比你小)。

令 $len = n - x$,$d = a_{x + 1} - a_x$,那么问题变成了数长度为 $len$,值域在 $[0, d]$ 的序列个数。

这个很经典,答案为 $\binom{d + len}{len}$。

考虑 $x = n$ 咋做。

也就是要求一个不降序列,减去一个不降序列还是不降序列。

这个限制就没有那么简单了。

但是还是可以考虑 $dp$ 并且使用前缀和优化做到 $O(nd)$。

设现在被减掉的序列是 $b$,那么 $b$ 序列合法的必要条件是:
$$
\forall i \in [1, x), a_i - b_i \geq a_{i-1} - b_{i-1}
$$
(不妨假设, $a_0 = b_0 = 0$)​

移项得到:

$$
\forall i \in [1, x), a_i - a_{i-1} \geq b_i - b_{i-1}
$$
不难发现这个条件也是充分的。

$a_i’ = a_i - a_{i-1}$,$b_i’ = b_i - b_{i-1}$,那么最终形式就是:

$$
\forall i \in [1, x), a_i’ \geq b_i’
$$
注意到 $b$ 和 $b’$ 一一对应,因此只需要算出 $b’$ 的方案数,就可以算出 $b$ 的方案数,而 $b’$ 的方案数显然是:

$$
\prod_{i=1}^{x-1} (a_i’ + 1)
$$
直接算即可。

最后只需要将前面部分和后面部分拼起来,就可以得到答案。

代码(Code)

44分
#include<bits/stdc++.h>
#include<vector>
#include<queue>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
#define P pair<int,int> 
#define int long long
#define x first
#define y second
inline int rd(){
	char c;bool f=false;while(!isdigit(c=getchar()))f=c=='-';int x=c^48;
	while(isdigit(c=getchar())){x=(((x<<2)+x)<<1)+(c^48);}return f?-x:x;
}const int inf = 0x3f3f3f3f3f3f3f3f;
const int N = 301234;
const int mod = 998244353;
bool ST;
inline void add(int &x,int y){if((x+=y)>=mod)x-=mod;}
inline void sub(int &x,int y){if((x-=y)<0)x+=mod;}

int n,p,a[N];
int f[5005][5005];

void Solve(){                       
	n=rd();For(i,1,n) a[i]=rd();p=rd();
	
	f[p][a[p]] = 1;
	Rof(i,p-1,1){
		int leijia = 0, tp = a[i+1];
		Rof(j,a[i+1],0){
			add(leijia, f[i+1][j]);
			while((i+1!=p) && a[i]-j > a[i+1]-tp) sub(leijia, f[i+1][tp]), tp--;
			f[i][j] = leijia;
		}
	}
	For(j,a[p],a[p+1]) f[p+1][j] = 1;
	For(i,p+2,n){
		int leijia = 0;
		Rof(j,a[i-1],0){
			add(leijia, f[i-1][a[i-1]-j]);
			f[i][a[i]-j] = leijia;
		}
	}int ans1=0, ans2=0;
	For(i,0,a[1]) add(ans1, f[1][i]);
	For(i,0,a[n]) add(ans2, f[n][i]);
	printf("%lld\n", ans1*ans2%mod);
}

bool ED;
signed main(){
	cerr<<abs(&ST-&ED)/1024./1024.<<"MB\n";
	freopen("fate.in","r",stdin);
	freopen("fate.out","w",stdout);
	int Tt=1;double Tim=clock();
	while(Tt--) Solve();
	return cerr<<"TIME:"<<(clock()-Tim)/CLOCKS_PER_SEC,0;
}
52分
#include<bits/stdc++.h>
#include<vector>
#include<queue>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
#define P pair<int,int> 
#define int long long
#define x first
#define y second
inline int rd(){
	char c;bool f=false;while(!isdigit(c=getchar()))f=c=='-';int x=c^48;
	while(isdigit(c=getchar())){x=(((x<<2)+x)<<1)+(c^48);}return f?-x:x;
}const int inf = 0x3f3f3f3f3f3f3f3f;
const int N = 301234;
const int mod = 998244353;
bool ST;
inline void add(int &x,int y){if((x+=y)>=mod)x-=mod;}
inline void sub(int &x,int y){if((x-=y)<0)x+=mod;}
inline void mul(int &x,int y){x=x*y%mod;}

int n,p,a[N];
int f[10005][10005];

void Solve(){                       
	n=rd();For(i,1,n) a[i]=rd();p=rd();
	
	f[p][a[p]] = 1;
	For(j,a[p],a[p+1]) f[p+1][j] = 1;
	For(i,p+2,n){
		int leijia = 0;
		Rof(j,a[i-1],0){
			add(leijia, f[i-1][a[i-1]-j]);
			f[i][a[i]-j] = leijia;
		}
	}
	
	int ans1=1, ans2=0;
	For(i,1,p-1) ans1 = ans1*(a[i]-a[i-1]+1)%mod;
	For(i,0,a[n]) add(ans2, f[n][i]);
	cerr<<ans1<<' '<<ans2<<'\n';
	printf("%lld\n", ans1*ans2%mod);
}

bool ED;
signed main(){
	cerr<<abs(&ST-&ED)/1024./1024.<<"MB\n";
	freopen("fate.in","r",stdin);
	freopen("fate.out","w",stdout);
	int Tt=1;double Tim=clock();
	while(Tt--) Solve();
	return cerr<<"TIME:"<<(clock()-Tim)/CLOCKS_PER_SEC,0;
}
100分
#include<bits/stdc++.h>
#include<vector>
#include<queue>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
#define P pair<int,int> 
#define int long long
#define x first
#define y second
inline int rd(){
	char c;bool f=false;while(!isdigit(c=getchar()))f=c=='-';int x=c^48;
	while(isdigit(c=getchar())){x=(((x<<2)+x)<<1)+(c^48);}return f?-x:x;
}const int inf = 0x3f3f3f3f3f3f3f3f;
const int N = 301234;
const int mod = 998244353;
bool ST;
inline void add(int &x,int y){if((x+=y)>=mod)x-=mod;}
inline void sub(int &x,int y){if((x-=y)<0)x+=mod;}
inline void mul(int &x,int y){x=x*y%mod;}
inline int ksm(int x,int k=mod-2){
	int res=1;while(k){
		if(k&1) mul(res,x);
		mul(x,x), k>>=1;
	}return res;
}int fac[N],inv[N];
inline int C(int n,int m){
	if(n==0 || m==0 || n==m) return 1;
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int n,x,a[N];

void Solve(){
	fac[0] = 1;For(i,1,N-5) fac[i] = fac[i-1]*i%mod;
	inv[N-5] = ksm(fac[N-5], mod-2);
	Rof(i,N-6,1) inv[i] = inv[i+1]*(i+1)%mod;
	
	n=rd();For(i,1,n) a[i]=rd();x=rd();
	int z = 1, y = 1;
	if(x < n){
		y = C(n-x+a[x+1]-a[x], n-x);
	}
	if(x > 1){
		for(int i=1;i<x;i++){
			z = z*(a[i]-a[i-1]+1)%mod;
		}
	}printf("%lld\n",z*y%mod);
}

bool ED;
signed main(){
	cerr<<abs(&ST-&ED)/1024./1024.<<"MB\n";
	freopen("fate.in","r",stdin);
	freopen("fate.out","w",stdout);
	int Tt=1;double Tim=clock();
	while(Tt--) Solve();
	return cerr<<"TIME:"<<(clock()-Tim)/CLOCKS_PER_SEC,0;
}

文章作者: WolfDeer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WolfDeer !
  目录