2022牛客寒假第二场 小沙的remake (排序+树状数组优化dp)

题目链接:点击这里

题目大意:
给定两个长度为 n n n 的序列 a [ i ] , b [ i ] a[i],b[i] a[i],b[i]
对于 a [ i ] a[i] a[i] 序列,我们按顺序选择,对于每个 a [ i ] a[i] a[i] 有选与不选两种选择,当且仅当 a [ i ] a[i] a[i] 比前面的选择的元素大 时才可被选择,选择还有一个位置限制:只有在 [ i − b i , i ) [i-b_i,i) [ibi,i) 下标内有元素被选择时 a [ i ] a[i] a[i] 才可被选择
对于选择方案来说,如果有一个选择不同即认为是一种不同的方案,求总方案数

题目分析:
d p [ i ] dp[i] dp[i] 表示以选择了第 i i i 个元素结束的方案数
其转移方程为 d p [ i ] = ∑ k = i − b i i − 1 [ a [ i ] > a [ k ] ] ⋅ d p [ k ] dp[i]=\sum_{k=i-b_i}^{i-1}[a[i]>a[k]]·dp[k] dp[i]=k=ibii1[a[i]>a[k]]dp[k] (其中中括号 [      ] [\;\;] [] 称为艾弗森括号,当括号内的表达式为真时,值为 1 1 1 否则为 0 0 0
我们可以先对 a [ i ] a[i] a[i] 进行排序,来保证当前所有的转移都满足 a [ i ] > a [ k ] a[i]>a[k] a[i]>a[k] ,然后把 d p dp dp 数组用一个树状数组维护区间和就可以来达到 log ⁡ n \log n logn 转移
总体时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

具体细节见代码:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<unordered_map>
#define ll long long
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
// #define int  ll
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
int read()
{
	int res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
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;
const int maxn = 2e6+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,seed,a[maxn],b[maxn],c[maxn];
int lowbit(int u)
{
	return u&(-u);
}
void add(int pos,int x)
{
	while(pos <= n) c[pos] = (c[pos]+x)%mod,pos += lowbit(pos);
}
int query(int pos)
{
	int res = 0;
	if(pos <= 0) return 0; 
	while(pos) res = (res+c[pos])%mod,pos -= lowbit(pos);
	return res;
}
pair<int,int>p[maxn];
int main()
{
	n = read(),seed = read();
	srand(seed);
	for(int i = 1;i <= n;i++) a[i] = read(0),b[i] = read(i);
	for(int i = 1;i <= n;i++) p[i].first = a[i],p[i].second = i;
	sort(p+1,p+n+1);
	for(int i = 1;i <= n;i++)
	{
		int id = p[i].second;
		int cur = (query(id-1)-query(id-b[id]-1)+1+mod)%mod;
		add(id,cur);
	}
	cout<<query(n)<<endl;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值