2023“钉耙编程”联赛 Day 3 E 题 - Out of Control 题解

文章描述了一个涉及云服务API的问题,该API用于存储用户的历史时间戳。每次API请求时,新的时间戳会被添加到堆栈尾部,除非它小于堆栈中的前一个时间戳。文章提出了在网络错误导致请求顺序混乱的情况下,如何计算可能的不同堆栈组合。解决方案涉及到动态规划和前缀和优化,以计算不同长度的不降序时间戳序列数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题描述

题面由我校翻译组激情制造。

有一个云服务 API\text{API}API 来帮助用户存储历史时间戳。

每个用户的数据结构最初是一个空堆栈。

每次向 API\text{API}API 发送带有整数 xxx (表示当前时间戳)的请求时,服务器都会将 xxx 附加到堆栈的末尾。

xxx 小于堆栈中存储的前一个时间戳时,服务器将认为输入是错误的,将加入前一个时间戳入堆栈而不是 xxx

您已经发布了 nnnAPI\text{API}API,您的请求时间戳在第 iii 次调用中是 xix_ixi

然而,网络已经失控。

服务器可能以任意顺序接收您的请求,甚至可能错过某些请求。

知道了这个问题,您要求随叫随到的工程师查看数据库中的堆栈。

假设服务器正好收到 kkk 个请求,那么它可能有多少不同的堆栈?

输入格式

第一行包含单个整数 TTT (1≤T≤100)(1≤T≤100)(1T100),测试用例的数量。对于每个测试用例:

输入的第一行包含单个整数 nnn (1≤n≤3000)(1≤n≤3000)(1n3000),表示请求总数。

第二行包含 nnn 个整数 x1,x2,…,xnx_1,x_2,\dots,x_nx1,x2,,xn (1≤xi≤109)(1≤x_i≤10^9)(1xi109),表示每个请求的时间戳。

可以保证所有 nnn 的和不超过 300003000030000

输出格式

对于每个测试用例,输出 nnn 行,其中第 iii (1≤i≤n)(1≤i≤n)(1in) 行包含一个整数,表示 k=ik=ik=i 时不同堆栈的数量。

请注意,答案可能非常大,所以请打印它模 109+710^9+7109+7 代替。

样例

样例输入

2
3
1 2 3
3
2 3 3

样例输出

3
5
5
2
2
2

样例解释

在第一个示例中:

k=1k=1k=1 时,栈可以是 [1],[2],[3][1],[2],[3][1],[2],[3]
k=2k=2k=2 时,栈可以是 [1,2],[1,3],[2,2],[2,3],[3,3][1,2],[1,3],[2,2],[2,3],[3,3][1,2],[1,3],[2,2],[2,3],[3,3]
k=3k=3k=3 时,栈可以是 [1,2,3],[1,3,3],[2,2,3],[2,3,3],[3,3,3][1,2,3],[1,3,3],[2,2,3],[2,3,3],[3,3,3][1,2,3],[1,3,3],[2,2,3],[2,3,3],[3,3,3]

在第二个示例中:
k=1k=1k=1 时,栈可以是 [2],[3][2],[3][2],[3]
k=2k=2k=2 时,栈可以是 [2,3],[3,3][2,3],[3,3][2,3],[3,3]
k=3k=3k=3 时,栈可以是 [2,3,3],[3,3,3][2,3,3],[3,3,3][2,3,3],[3,3,3]

题目分析

首先发现我们不关心值的大小,只关心排名,而且貌似需要嗯造到动态规划状态里,所以需要离散化。

我们考虑转换问题,发现类似于填充一个不下降子序列,但是有点限制。

接下来考虑动态规划,我们设 fi,jf_{i,j}fi,j 表示填到第 iii 位,末尾为 jjj

边界比较清楚,f1,j=1f_{1,j}=1f1,j=1

我们发现这个填充并不是没有条件的,事实上,我们发现 i≤tji\leq t_jitjtjt_jtj 表示有多少数字小于等于 jjj,因为我们在前面填出的多的 jjj 本质是拿比 jjj 小的数字代替,因此怎么填也填不到 tjt_jtj 后面。

我们不难得到转移方程:

fi,j=∑k=1jfi−1,k(i≤tj) f_{i,j}=\sum_{k=1}^jf_{i-1,k}(i\leq t_j) fi,j=k=1jfi1,k(itj)

条件不成立则值为 000

不得不说,这个还是比较好想的,但是我们需要优化,加个前缀和即可。

我们用定义一个 sumsumsum,有:

sumi,j=∑k=1jfi,j sum_{i,j}=\sum_{k=1}^j f_{i,j} sumi,j=k=1jfi,j

以上方程可以写成:

fi,j=sumi−1,j(i≤tj) f_{i,j}=sum_{i-1,j}(i\leq t_j) fi,j=sumi1,j(itj)

对于第 iii 个输出:

ansi=∑j=1Mfi,j ans_i=\sum_{j=1}^M f_{i,j} ansi=j=1Mfi,j

这里的 MMM 表示离散化之后的最大值。

实现代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=3e3+5;
const LL mod=1e9+7;
LL T,n,x[N],a[N],t[N],f[N][N],sum[N][N],cnt,ans[N];
int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld",&n);
		memset(t,0,sizeof(t));
		memset(f,0,sizeof(f));
		memset(sum,0,sizeof(sum));
		memset(ans,0,sizeof(ans));
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&x[i]);
		}
		sort(x+1,x+n+1);
		cnt=0;
		for(int i=1;i<=n;i++)
		{
			if(x[i]!=x[i-1])cnt++;
			a[i]=cnt;
			t[cnt]++;
		}

		for(int i=1;i<=cnt;i++)t[i]+=t[i-1];
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=cnt;j++)
			{
				if(i==1)f[i][j]=1;
				else if(i<=t[j])f[i][j]=sum[i-1][j];
				else f[i][j]=0;
				ans[i]=(ans[i]+f[i][j])%mod;
				sum[i][j]=(sum[i][j-1]+f[i][j])%mod;
			}
		}
		for(int i=1;i<=n;i++)
		{
			printf("%lld\n",ans[i]);
		}
	}
}

如果你看完了整个文章,下面有投票可以玩,再奖励你看一个科比。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值