P4229 某位歌姬的故事 题解
目录
题目描述
组数据,求有多少个长为 的数组 满足 和以下 条限制:
对 取模。
数据范围
- 。
- 。
- 。
时间限制 ,空间限制 。
分析
很大但是没有什么用,将限制写成开区间后,序列被切分成 个连续段。一个长为 的连续段最大值为 的方案数为 。
先考虑 的部分分。
记 为 中,最后一个最大值为 的连续段编号的最小值。对每个限制 ,令 。
表示考虑 ,且第 个连续段最大值为 的方案数。枚举上一个 出现的位置,有 ,其中 为第 个连续段最大值为 的方案数。前缀和优化即可做到 。
回到原题,按照 从小到大考虑每个限制(神来之笔!),将 相同的限制放在一起考虑。
显然这些限制导致每个连续段有各自的上界,扔掉所有上界 的区间,对于所有上界 的区间,将最大值 视为 ,最大值 视为 ,于是转化成了 的问题!
只不过现在填 和 都带权( 时填 的方案唯一),分别记为 。令 ,最后乘上 即可。
用 set
/并查集维护上界为 的区间,时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005,mod=998244353;
int a,m,n,t,q,res;
int c[maxn],f[maxn],s[maxn],dp[maxn],id[maxn],lim[maxn];
struct seg
{
int l,r,w;
}o[maxn];
int qpow(int a,int k)
{
int res=1;
for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
return res;
}
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
void work()
{
scanf("%d%d%d",&n,&q,&a),m=0,res=1;
for(int i=1;i<=q;i++) scanf("%d%d%d",&o[i].l,&o[i].r,&o[i].w),c[++m]=o[i].l,c[++m]=++o[i].r;
c[++m]=1,c[++m]=n+1,sort(c+1,c+m+1),m=unique(c+1,c+m+1)-c-1,iota(f+1,f+m+1,1);
for(int i=1;i<=q;i++) o[i].l=lower_bound(c+1,c+m+1,o[i].l)-c,o[i].r=lower_bound(c+1,c+m+1,o[i].r)-c;
sort(o+1,o+q+1,[&](seg x,seg y){return x.w<y.w;});
for(int l=1,r=0;l<=q;l=r+1)
{
for(r=l;r<q&&o[r+1].w==o[l].w;r++) ;
int k=0;
for(int i=l;i<=r;i++) for(int j=find(o[i].l);j<o[i].r;j=find(j)) f[j]=j+1,id[++k]=j;
sort(id+1,id+k+1),memset(lim+1,0,4*(k+1));
for(int i=l;i<=r;i++)
{
int _l=lower_bound(id+1,id+k+1,o[i].l)-id,_r=lower_bound(id+1,id+k+1,o[i].r)-id;
if(_l==_r) return printf("0\n"),void();
lim[_r]=max(lim[_r],_l);
}
if(o[l].w==1) continue;///o[l].w=1时方案唯一且v1=0,所以不能用后面的dp
dp[0]=s[0]=1;
for(int i=1;i<=k+1;i++)
{///添加虚点k+1,钦定k+1必填2,方案数v2=1
lim[i]=max(lim[i],lim[i-1]);///i的限制不弱于i-1的限制
int len=i<=k?c[id[i]+1]-c[id[i]]:1;
int v1=qpow(o[l].w-1,len),v2=qpow(o[l].w,len)-v1,val=1ll*(v2+mod)*qpow(v1,mod-2)%mod;
res=1ll*res*v1%mod;
dp[i]=1ll*val*(s[i-1]-(lim[i]?s[lim[i]-1]:0)+mod)%mod,s[i]=(s[i-1]+dp[i])%mod;
}
res=1ll*res*dp[k+1]%mod;
}
int len=0;
for(int i=1;i<m;i++) if(f[i]==i) len+=c[i+1]-c[i];
printf("%lld\n",1ll*res*qpow(a,len)%mod);
}
int main()
{
for(scanf("%d",&t);t--;) work();
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/18652150
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现