P4229 某位歌姬的故事 题解

题目描述

\(T\) 组数据,求有多少个长为 \(n\) 的数组 \(h\) 满足 \(1\le h_i\le a\) 和以下 \(q\) 条限制:

\[\max_{l_i\le j\le h_i}h_j=w_i \]

\(998244353\) 取模。

数据范围

  • \(1\le T\le 20\)
  • \(1\le n,a\le 9\cdot 10^8,1\le q\le 500\)
  • \(1\le l_i\le r_i\le n,1\le m_i\le a\)

时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{512MB}\)

分析

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

先考虑 \(a=2,w_i=2\) 的部分分。

\(lim_i\)\([1,i-1]\) 中,最后一个最大值为 \(2\) 的连续段编号的最小值。对每个限制 \((l,r,w)\) ,令 \(lim_{r+1}\gets\max(lim_{r+1},l)\)

\(f_i\) 表示考虑 \([1,i]\) ,且第 \(i\) 个连续段最大值为 \(2\) 的方案数。枚举上一个 \(2\) 出现的位置,有 \(f_i=val_i\cdot\sum_{j=lim_i}^{i-1}f_j\) ,其中 \(val_i\) 为第 \(i\) 个连续段最大值为 \(2\) 的方案数。前缀和优化即可做到 \(\mathcal O(Tq)\)


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

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

只不过现在填 \(1\)\(2\) 都带权(\(a=2\) 时填 \(1\) 的方案唯一),分别记为 \(v_{1,i},v_{2,i}\) 。令 \(val_i=\frac{v_{2,i}}{v_{1,i}}\) ,最后乘上 \(\prod v_{1,i}\) 即可。

set/并查集维护上界为 \(w\) 的区间,时间复杂度 \(\mathcal O(Tq\log q)\)

#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 @ 2025-01-04 17:37  peiwenjun  阅读(1)  评论(0编辑  收藏  举报