潜龙未见静水流,沉默深藏待时秋。一朝破空声势振,惊世骇俗展雄猷。
随笔 - 82, 文章 - 0, 评论 - 3, 阅读 - 2163

P4229 某位歌姬的故事 题解

目录

题目描述

T 组数据,求有多少个长为 n 的数组 h 满足 1hia 和以下 q 条限制:

maxlijhihj=wi

998244353 取模。

数据范围

  • 1T20
  • 1n,a9108,1q500
  • 1lirin,1mia

时间限制 1s ,空间限制 512MB

分析

n 很大但是没有什么用,将限制写成开区间后,序列被切分成 O(q) 个连续段。一个长为 len 的连续段最大值为 x 的方案数为 xlen(x1)len

先考虑 a=2,wi=2 的部分分。

limi[1,i1] 中,最后一个最大值为 2 的连续段编号的最小值。对每个限制 (l,r,w) ,令 limr+1max(limr+1,l)

fi 表示考虑 [1,i] ,且第 i 个连续段最大值为 2 的方案数。枚举上一个 2 出现的位置,有 fi=valij=limii1fj ,其中 vali 为第 i 个连续段最大值为 2 的方案数。前缀和优化即可做到 O(Tq)


回到原题,按照 w 从小到大考虑每个限制(神来之笔!),将 w 相同的限制放在一起考虑。

显然这些限制导致每个连续段有各自的上界,扔掉所有上界 <w 的区间,对于所有上界 =w 的区间,将最大值 <w 视为 1 ,最大值 =w 视为 2 ,于是转化成了 a=2 的问题!

只不过现在填 12 都带权(a=2 时填 1 的方案唯一),分别记为 v1,i,v2,i 。令 vali=v2,iv1,i ,最后乘上 v1,i 即可。

set/并查集维护上界为 w 的区间,时间复杂度 O(Tqlogq)

#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;
}

posted on   peiwenjun  阅读(4)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示