小沙的remake(竞速版)题解(乱序dp+树状数组)

题目链接

题目思路

一个简单的想法就是设\(dp[i]\)表示以\(i\)结尾的答案

那么转移就是找\([i-b[i],i-1]\)中所有\(a[j]<a[i]\)的值\(dp[i]+=dp[j]\)

最后\(dp[i]++\) 表示以\(i\)开头,那么转移就是找一个区间中大于一个数的所有\(dp\)值之和\(+1\)

那么不就是主席树吗

但是可以进行优化,可以不要从后往前\(dp\),而是按照\(a[i]\)的大小排序进行\(dp\)

按照权值的大小从小到大插入选取该点的方案数到对应下标上,所以的话对于权值比你大的点,你没有进行计算,

且求和过程中可以计算区间的和。全部插入之后输出1~n的和即可。

相对于传统的DP,这里使用的是一种相对乱序的DP转移方式

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
namespace GenHelper
{
    int z1,z2,z3,z4,z5,u,res;
    int get()
    {
        z5=((z1<<6)^z1)>>13;
        z1=((int)(z1&4294967)<<18)^z5;
        z5=((z2<<2)^z2)>>27;
        z2=((z2&4294968)<<2)^z5;
        z5=((z3<<13)^z3)>>21;
        z3=((z3&4294967)<<7)^z5;
        z5=((z4<<3)^z4)>>12;
        z4=((z4&4294967)<<13)^z5;
        return (z1^z2^z3^z4);
    }
    int read(int m) {
        u=get();
        u>>=1;
        if(m==0)res=u;
        else res=(u/2345+1000054321)%m;
        return res;
    }
     void srand(int x)
    {
        z1=x;
        z2=(~x)^(0x23333333);
        z3=x^(0x12345798);
        z4=(~x)+51;
      	u = 0;
    }
}
using namespace GenHelper;
using namespace std;
const int N=2e6+7,mod=1e9+7;
int a[N],b[N];
pair<int,int> pa[N];
int n,seed;
ll f[N];
int query(int x){
    int res=0;
    while(x) (res+=f[x])%=mod,x-=x&-x;
    return res;
}
void add(int x,int y){
    while(x<=n) (f[x]+=y)%=mod,x+=x&-x;
}
int main(){
    scanf("%d %d",&n,&seed);
	srand(seed);
	for(int i=1;i<=n;i++){
		a[i]=read(0),b[i]=read(i);
		pa[i]={a[i],i};
	}
    sort(pa+1,pa+1+n);
    ll pr=0;
    for(int i=1;i<=n;i++){
        ll tmp=query(pa[i].se)-query(max(0,pa[i].se-b[pa[i].se]-1))+1;
        tmp=(tmp%mod+mod)%mod;
        add(pa[i].se,tmp);
        pr=(pr+tmp)%mod;
    }
    printf("%lld\n",pr);
    return 0;
}

posted @ 2022-01-27 12:40  hunxuewangzi  阅读(63)  评论(0编辑  收藏  举报