题目链接
题目思路
一个简单的想法就是设\(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;
}