【2022寒假基础集训】第二场 -L/M.小沙的remake【BIT+DP】

Date:2022.03.12
题目描述:
小沙的人生充满坎坷,他现在准备remake,在新的人生中他想要自己的人生一帆风顺,步步高升,所以他希望自己的气运不会下降,他的新的人生,活的时间可以不长,但一定要步步高升。
现在有 n n n个气运放在小沙面前,小沙只能按着顺序选择,对于一个气运,他只能选择选或不选,并且他希望这个气运不能比之前已经选择的低,现在小沙想问你,小沙生成的新的人生有多少种可能。
对于每个气运来说,每个气运都有自己的依赖性,他希望选择的气运的前 b i b_i bi 个以内至少存在一个已被选择的气运(也就是下标为 [ i − b , i ) [i-b,i) [ib,i),要是超出了这个范围,他们就不愿意被你选择,从而自主消失,当然,如果你第一个选择他,他也是愿意被你选择的。
对于每个气运,小沙如果不选就会消失,并不能之后在回头来选。
对于小沙选择的气运序列,只要有一个不同,他就是一种新的人生。
由于答案比较大,所以小沙希望你对1e9+7取模。

对于C/C++语言
#include<bits/stdc++.h> 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 (z1z2z3^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]; int main(){
int n,seed;
scanf("%d %d",&n,&seed);
srand(seed);
for(int i=1;i<=n;i++){
a[i]=read(0),b[i]=read(i);
}
return 0; }

JAVA版代码 import java.util.; import java.math.; public class Main{
static int z1,z2,z3,z4,z5,u,res;
public static 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 (z1z2z3^z4);
}
public static int read(int m){
u=get();
u>>=1;
if(m==0)res=u;
else res=(u/2345+1000054321)%m;;
return res;
}
public static void ssrand(int x){
z1=x;
z2=(~x)^(0x23333333);
z3=x^(0x12345798);
z4=(~x)+51;
u = 0;
}
public static void main(String args[]) {
Scanner in=new Scanner(System.in);
int a[]=new int[2000010];
int b[]=new int[2000010];
int n=in.nextInt(),seed=in.nextInt();
ssrand(seed);
for(int i=1;i<=n;i++){
a[i]=read(0);
b[i]=read(i);
}
} }

Python代码: 不会写你们对着自己写写吧QAQ。

输入描述:
第一行输入两个整数n,seed。
1 < = n < = 2 × 1 0 6 , 0 < = b i < i 1<=n<=2\times10^6,0<=b_i<i 1<=n<=2×106,0<=bi<i
输出描述:
输出一个数代表答案
示例1
输入
复制
3 2
输出
复制
4
说明
得到的数为
8803593 0
3293267 0
268485133 1
可以看出答案为4个
选第一个 不选第二个 不选第三个
不选第一个 选第二个 不选第三个
不选第一个 选第二个 选第三个
不选第一个 不选第二个 选第三个
错误示范:
选第一个 不选第二个 选第三个
虽然第三个比第一个大,但是没有选择第二个,并且第三个不是第一个选择,所以不能选择第三个

思路:看样子是dp,首先搞清楚一个点(下标为 i i i)会被选择的两种情况:
①这是被选的第1个点。(显然这就是一种方案。)
②其前面 [ i − b i , i ) [i-b_i,i) [ibi,i)区间中已被选择了若干个点,并且后被选的点权值一定 > = >= >=先被选的点。
这就给了我们一个排序的思路:
①标记每个点的原始下标。
②按每个点的值为第一要素排序。
状态方程: f [ i ] : f[i]: f[i]:以原始下标 i i i为最后一个选择的人生有多少种方案。
由此保证:权值上 前 < = 后 前<=后 <=。如果能保证下标也是 前 < 后 前<后 <,那么问题可以转化为 f [ i ] = f [ i − b i − 1 ] + . . . + f [ i − 1 ] f[i]=f[i-b_i-1]+...+f[i-1] f[i]=f[ibi1]+...+f[i1],即区间求和,因此既能满足使下标递增也能快速区间求和显然用BIT优化!每次快速求和后赋值给新的 f [ i ] f[i] f[i],最终结果即为 g e t s u m ( n ) = = f [ 1 ] + f [ 2 ] . . . + f [ n ] getsum(n)==f[1]+f[2]...+f[n] getsum(n)==f[1]+f[2]...+f[n]

代码如下:

#include<bits/stdc++.h>
#define x first
#define y second
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;
typedef long long LL;
typedef pair<int,int> PII;
int a[N],b[N],n,seed;
LL tr[N];//dp的状态方程,表示:
PII s[N];
int lowbit(int x) {return x&-x;}
void add(LL x,LL c)
{
    for(int i=x;i<=n;i+=lowbit(i)) tr[i]=(tr[i]+c)%mod;
}
LL getsum(LL x)
{
    LL res=0;
    for(int i=x;i;i-=lowbit(i)) res=(res+tr[i])%mod;
    return res;
}
int main()
{
    scanf("%d %d",&n,&seed);
    srand(seed);
    for(int i=1;i<=n;++i)
    {
        a[i]=read(0),b[i]=read(i);
        s[i].x=a[i];s[i].y=i;
    }
    sort(s+1,s+1+n);
    for(int i=1;i<=n;++i)
    {
        LL k=((getsum(s[i].y-1)-getsum(s[i].y-b[s[i].y]-1))%mod+1+mod)%mod;//[i-bi,i)与第一个选i的方案和。
        add(s[i].y,k);//赋给当前原坐标,是一个dp的过程
    }
    printf("%lld",getsum(n));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值