4574 [Zjoi2016]线段树 dp

这题的前缀和开始没有想到。
如果不用前缀和状态是五维的。。。

由于数据随机所以不会有两个相等的数。
最终答案是求每个数在所有方案下的取值的和。
对于每个数dp一下,设当前数为val,区间[L,R]为以当前数为最大值的最大区间。
f[i][l][r] 表示前i个询问,区间[L,R]剩余的小于等于当前数的区间为[l,r]的方案数。
然后转移时处理一个前缀和可以O(1)转移。
对于[L,R]内的每一个点x,把 (val,lx,rxf[q][l][r]) 加入x(表示x最终取值小于等于当前数的方案数为 lx,rxf[q][l][r] )然后最后通过前缀和相减得到每个数最终每个取值的次数。

由于数据随机所以每个数的区间[L,R]的期望长度是O(logn)的。
不过最大的数区间长度一定为n,因此总复杂度 O(n2q)

#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long
#define N 410
int n,q;
int a[N],f[2][N][N],sum1[2][N][N],sum2[2][N][N];
int cnt[N],tmp[N],ans[N];
vector<pair<int,int> >vec[N];
void clear(int x,int l,int r)
{
    for(int i=l;i<=r;i++)
        for(int j=i;j<=r;j++)
            f[x][i][j]=sum1[x][i][j]=sum2[x][i][j]=0;
}
void getsum(int x,int l,int r)
{
    for(int i=l;i<=r;i++)
        for(int j=l;j<=i;j++)
            sum1[x][j][i]=(sum1[x][j-1][i]+(ll)f[x][j][i]*(j-1))%mod;
    for(int i=l;i<=r;i++)
        for(int j=r;j>=i;j--)
            sum2[x][i][j]=(sum2[x][i][j+1]+(ll)f[x][i][j]*(n-j))%mod;
}
void cal(int v,int x,int l,int r)
{
    for(int i=l;i<=r;i++)tmp[i]=0;
    for(int i=l;i<=r;i++)
        for(int j=r;j>=i;j--)
            f[x][i][j]=(f[x][i][j]+f[x][i][j+1])%mod;
    for(int i=l;i<=r;i++)
        for(int j=l;j<=i;j++)
            tmp[i]=(tmp[i]+f[x][j][i])%mod;
    for(int i=l;i<=r;i++)
        vec[i].push_back(make_pair(v,tmp[i]));
}
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)cnt[i]=(ll)i*(i+1)/2%mod;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int now=1;now<=n;now++)
    {
        int L=now-1,R=now+1;
        for(;L>0&&a[L]<a[now];L--);L++;
        for(;R<=n&&a[R]<a[now];R++);R--;
        f[0][L][R]=1;getsum(0,L,R);
        for(int i=1;i<=q;i++)
        {
            for(int j=L;j<=R;j++)
                for(int k=j;k<=R;k++)
                    f[i&1][j][k]=((ll)f[~i&1][j][k]*((cnt[j-1]+cnt[k-j+1])%mod+cnt[n-k])
                    +sum1[~i&1][j-1][k]+sum2[~i&1][j][k+1])%mod;
            getsum(i&1,L,R);
        }
        cal(a[now],q&1,L,R);
        clear(0,L,R);clear(1,L,R);
    }
    for(int i=1;i<=n;i++)
    {
        sort(vec[i].begin(),vec[i].end());
        ans[i]=(ans[i]+(ll)vec[i][0].second*vec[i][0].first%mod)%mod;
        for(int j=1;j<vec[i].size();j++)
            ans[i]=(ans[i]+(ll)(vec[i][j].second-vec[i][j-1].second+mod)*vec[i][j].first)%mod;
        printf("%d%c",ans[i],i==n ? '\n':' ');
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值