loj#2331. 「清华集训 2017」某位歌姬的故事【动态规划】

传送门

解题思路:

将序列离散化后,可以给每个点确定一个取值的上界,那么每一种上届的贡献是独立的,分别求出再相乘即可。
对于一种上界 w w ,把对应的点和限制提出,那么对于每个右端点,唯一有用的限制就是左端点最靠右的,记为L[i],再设 f[i][j] f [ i ] [ j ] 表示到第 i i 个点,最后一个 w j j <script type="math/tex" id="MathJax-Element-36">j</script> 的方案数转移即可。
由于要离散化,所以细节比较多。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=1005,INF=1e9,mod=998244353;
int T,n,m,A,mx,num,p[N],w[N],t[N],a[N],L[N],f[N][N];ll ans;
struct data{int l,r,w;}q[N];
ll Pow(ll x,int y)
{
    ll res=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)res=res*x%mod;
    return res;
}
void calc(int W)
{
    num=0;
    for(int i=1;i<=n;i++)if(a[i]==W)
        t[++num]=i,L[num]=0,memset(f[num],0,sizeof(f[num]));
    for(int i=1;i<=m;i++)if(q[i].w==W)
    {
        if(!num){ans=-1;return;}
        int l=lower_bound(t+1,t+num+1,q[i].l)-t;
        int r=lower_bound(t+1,t+num+1,q[i].r)-t-1;
        L[r]=max(L[r],l);
    }
    f[0][0]=1;
    for(int i=1;i<=num;i++)
    {
        ll v1=Pow(W,p[t[i]+1]-p[t[i]]),v2=Pow(W-1,p[t[i]+1]-p[t[i]]);
        for(int j=0;j<i;j++)if(f[i-1][j])
        {
            if(j>=L[i])f[i][j]=(f[i][j]+f[i-1][j]*v2)%mod;
            f[i][i]=(f[i][i]+f[i-1][j]*(v1+mod-v2))%mod;
        }
    }
    ll res=0;
    for(int i=0;i<=num;i++)res=(res+f[num][i])%mod;
    ans=ans*res%mod;
}
void solve()
{
    n=getint(),m=getint(),A=getint();
    mx=num=0;
    for(int i=1;i<=m;i++)
    {
        q[i].l=getint(),q[i].r=getint()+1,q[i].w=getint();
        p[++num]=q[i].l,p[++num]=q[i].r,w[++mx]=q[i].w;
    }
    p[++num]=1,p[++num]=n+1,sort(p+1,p+num+1),n=unique(p+1,p+num+1)-p-1;
    sort(w+1,w+mx+1),mx=unique(w+1,w+mx+1)-w-1;
    for(int i=1;i<=n;i++)a[i]=INF;
    for(int i=1;i<=m;i++)
    {
        q[i].l=lower_bound(p+1,p+n+1,q[i].l)-p;
        q[i].r=lower_bound(p+1,p+n+1,q[i].r)-p;
        for(int j=q[i].l;j<q[i].r;j++)a[j]=min(a[j],q[i].w);
    }
    ans=1;
    for(int i=1;i<=mx;i++)
    {
        calc(w[i]);
        if(ans==-1){puts("0");return;}
    }
    for(int i=1;i<n;i++)if(a[i]==INF)
        ans=ans*Pow(A,p[i+1]-p[i])%mod;
    cout<<ans<<'\n';
}
int main()
{
    //freopen("lx.in","r",stdin);
    for(int T=getint();T;T--)solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值