专栏文章
题解:P13662 「TPOI-5A」Luminescence
P13662题解参与者 3已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 3 条
- 当前快照
- 1 份
- 快照标识符
- @miogaaip
- 此快照首次捕获于
- 2025/12/02 18:43 3 个月前
- 此快照最后确认于
- 2025/12/02 18:43 3 个月前
首先观察题目,很难不看出 对应的是前缀最小值, 对应的是后缀最小值。
那么一个合法的 数组必然是单调不升的,且对于每个 的位置,一定有 。而对于 的位置,则有 的限制。
同理,一个合法的 数组必然单调不降,且对于每个 的位置,一定有 。而对于 的位置,则有 的限制。
另外,考虑数字 出现的位置 ,其会导致 时 且 时 。所以有且仅有一个位置会满足 。
到这里,我们完成了是否存在合法排列的判断。如果只是计算合法排列的数量,我们发现对于每个不确定 的位置 ,都会有形如 的限制。那么按照这个限制降序排序,根据当前可用的数字个数以及已经用掉的数字个数,很容易根据乘法原理计算出合法的方案数。
接下来考虑题目中给出的权值信息有什么用,可以看出其含义是计算所有区间的 值并求和。
那么考虑枚举 的值,变成对 ,去计算有多少区间满足 并乘上贡献进行求和。再采用经典技巧,变成对每个 ,计算有多少区间满足 并求和。即利用 对式子进行拆分。
也就是:。
对于统计有多少区间满足 ,则可以转化成有多少区间包含了 在内的所有数,进一步变成考虑 内数字出现的最左边位置 和最右边位置 ,那么对应方案数就是 (下标从 开始)。
到这里好像进行不下去了,我们考虑充分利用题目性质。注意到对于某个 ,若其没有出现在数组 中,这就意味着存在一个 的数出现在了 的左边,且存在一个 的数出现在了 的右边。分别考虑 中 的最大数字出现的位置,以两个位置为左右端点的区间一定是包含 在内的所有数的最小区间。否则若存在 的一个数 不在这一区间内,那么这个数 一定会作为前缀最小值或者后缀最小值出现在 内,进一步会出现在 中不在区间内的某个位置,产生矛盾。
于是对于每个 ,我们可以计算出包含 内所有数的最小区间 ,从而计算出对应权值。同时我们会发现,对于给定的 ,一个合法排列的权值是确定的,于是我们只需计算出对应权值,以及合法排列的方案数就能计算出最终答案。
实现方面,在计算方案数时可以采用双指针来规避排序部分,同时也能完成权值的计算,时间复杂度 。
CPP#include<bits/stdc++.h>
using namespace std;
#define N 200020
#define MOD 998244353
int T,n,a[N],b[N];
int main()
{
scanf("%d",&T);
while(T--){
int flg=1;
scanf("%d",&n);
a[0]=b[n+1]=n;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1;i<=n;i++)if(a[i]>a[i-1])flg=0;
for(int i=1;i<=n;i++)if(b[i]>b[i+1])flg=0;
int l=1,r=n,m=-1;
for(int i=1;i<=n;i++)if(b[i]==0)m=i;
if(m==-1 || a[m]>0 || a[m-1]==0)flg=0;
if(!flg){
printf("0\n");
continue;
}
int ans=0,tot=1,lst=n,cnt=0;
while(l<=r){
int x=max(a[l],b[r]),t=1ll*l*(n-r+1)%MOD;
ans+=1ll*t*(lst-x)%MOD,ans%=MOD;
cnt+=lst-x-1;
lst=x;
if(l==r)break;
if(a[l]>b[r]){
l++;
while(a[l]==x && l<=n)l++,tot=1ll*tot*cnt%MOD,cnt--;
}
else{
r--;
while(b[r]==x && r>=1)r--,tot=1ll*tot*cnt%MOD,cnt--;
}
}
printf("%lld\n",1ll*tot*ans%MOD);
}
}
相关推荐
评论
共 3 条评论,欢迎与作者交流。
正在加载评论...