【CDQ分治】Non-Decreasing Subsequences

CDQ分治好题,至少对于我这个CDQ分治初学者

loj3247

题目:

求区间不下降子序列个数
a i ≤ k , k ≤ 20 , N ≤ 5 × 1 0 4 , Q ≤ 2 × 1 0 5 a_i\leq k,k\leq 20,N\leq 5\times 10^4,Q\leq 2\times 10^5 aik,k20,N5×104,Q2×105

题解:

很难想到,要用CDQ分治
要分了之后怎么进行维护复杂度尽量小,这是CDQ分治很难受的地方。

  • 定义数组
    在每个 s o l v e ( l , r ) solve(l,r) solve(l,r)中都有
    f i , j 表 示 f_{i,j}表示 fi,j
    ( l ≤ j ≤ m i d ) j 为 左 端 点 , k ∈ [ j , m i d ] 中 所 有 a k = i 的 k 为 右 端 点 , 不 下 降 子 序 列 个 数 (l\leq j \leq mid)j为左端点 , k\in [j,mid]中所有a_{k}=i的k为右端点,不下降子序列个数 (ljmidjk[j,mid]ak=ik
    ( m i d < j ≤ r ) j 为 右 端 点 , k ∈ ( m i d , j ] 中 所 有 a k = i 的 k 为 左 端 点 , 不 下 降 子 序 列 个 数 (mid<j \leq r)j为右端点 , k\in (mid,j]中所有a_{k}=i的k为左端点,不下降子序列个数 (mid<jrjk(mid,j]ak=ik
  • 维护数组
    尽可能的找数组之间的联系
    首先因为 m i d mid mid f i , j f_{i,j} fi,j分为了两个部分,意义不同(其实泛泛来说差不多),所以我们也分两部分讨论
    这里我们先只分析右边的,左边的同理理解
    f i , j =   [ a j = = i ] + ∑ m i d ≤ k < j , a k ≤ a j f i , k f_{i,j}= \ [a_j==i]+\sum_{mid\leq k<j,a_k\leq a_j}f_{i,k} fi,j= [aj==i]+midk<j,akajfi,k
    然后我开始看到这式子,是懵逼的
    [ a j = = i ] [a_j==i] [aj==i]指如果 a j = = i a_j==i aj==i的话, j − > j j->j j>j就是一个合法区间, f i , j + 1 f_{i,j}+1 fi,j+1
    ∑ m i d ≤ k < j , a k ≤ a j f i , k \sum_{mid\leq k<j,a_k\leq a_j}f_{i,k} midk<j,akajfi,k指与前面的已合法区间拼接,因为还要满足拼接后不下降,所以 a k ≤ a j a_k\leq a_j akaj
    于是我们可以想到树状数组维护(略)
    意右边循环顺序是 m i d + 1 − > r mid+1->r mid+1>r,但左边循环顺序是 m i d − > l mid->l mid>l,因为是逆着来的,所以注意树状数组也要逆一下
注意:

因为空集也算合法区间,所以要把 f 1 , m i d , f k , m i d + 1 f_{1,mid},f_{k,mid+1} f1,mid,fk,mid+1都要+1

  • 询问求值
    yy一下我们想到,我们需要将我们的 f i , j f_{i,j} fi,j前缀和一下(左边是后缀和)
    我们先只考虑跨过 m i d mid mid的询问区间(ql,qr分别为询问的左右端点)
    a n s = ∑ 1 ≤ i ≤ k F i , q l ∑ i ≤ j ≤ k F j , q r ans=\sum_{1\leq i\leq k}F_{i,ql}\sum_{i\leq j\leq k}F_{j,qr} ans=1ikFi,qlijkFj,qr
    理解下,就是左右两端拼接,乘法原理
    (我曾迷惑: q l − > s , s − > q r ql->s,s->qr ql>s,s>qr m i d < s mid<s mid<s的情况没有考虑啊,但其实将 s s s移到 s ‾ < m i d \overline{s}<mid s<mid q l − > s ‾ , s ‾ < q r ql->\overline{s},\overline{s}<qr ql>s,s<qr这样是等效的)
  • 复杂度 O ( n k l o g n l o g k + q k ) O(nklognlogk+qk) O(nklognlogk+qk)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const int N=5e4+10,K=25,Q=2e5+10;
int n,k,q;
int a[N];
struct qus{int l,r;}qs[Q];
ll ans[Q];
ll f[K][N];
//---
ll t[K];
int lowbit(int x){return x&(-x);}
void clear(){memset(t,0,sizeof(t));}
void add(int i,int x){for(;i<=k;i+=lowbit(i))t[i]=(t[i]+x)%mod;}
ll qur(int i){ll res=0;for(;i;i-=lowbit(i))res=(res+t[i])%mod;return res;}
//----
ll ls[N];
void solve(int l,int r,vector<ll>v)
{
	if(l==r)
	{
		for(int i=0;i<v.size();i++)ans[v[i]]=2;
		return ;
	}
	int mid=(l+r)>>1;
	for(int i=1;i<=k;i++)
	{
		clear();
		for(int j=mid;j>=l;j--)
		{
			f[i][j]=qur(k+1-a[j]);
			if(a[j]==i)f[i][j]=(f[i][j]+1)%mod;//自己->自己 
			add(k+1-a[j],f[i][j]);//逆转树状数组 
		}
		for(int j=mid-1;j>=l;j--)f[i][j]=(f[i][j]+f[i][j+1])%mod;//前缀和 
	}
	for(int j=l;j<=mid;j++)f[1][j]=(f[1][j]+1)%mod;//空集
	for(int i=1;i<=k;i++)
	{
		clear();
		for(int j=mid+1;j<=r;j++)
		{
			f[i][j]=qur(a[j]);
			if(a[j]==i)f[i][j]=(f[i][j]+1)%mod;
			add(a[j],f[i][j]);
		}
		for(int j=mid+2;j<=r;j++)f[i][j]=(f[i][j]+f[i][j-1])%mod;
	}
	for(int j=mid+1;j<=r;j++)f[k][j]=(f[k][j]+1)%mod;
	vector<ll>vl,vr;
	vl.resize(0);vr.resize(0); 
	for(int i=0;i<v.size();i++)
	{
		int l=qs[v[i]].l,r=qs[v[i]].r;
		if(r<=mid)	  vl.push_back(v[i]);
		else if(mid<l)vr.push_back(v[i]);
		else
		{
			for(int j=k;j>=1;j--)ls[j]=(ls[j+1]+f[j][r])%mod;//前缀又后缀。。。
			for(int j=1;j<=k;j++)ans[v[i]]=(ans[v[i]]+ls[j]*f[j][l]%mod)%mod;
		}
	}
	if(vl.size())solve(l,mid,vl);
	if(vr.size())solve(mid+1,r,vr);
}
vector<ll>v;
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	scanf("%d",&q);
	for(int i=1;i<=q;i++)scanf("%d%d",&qs[i].l,&qs[i].r);
	for(int i=1;i<=q;i++)v.push_back(i);
	solve(1,n,v);
	for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
}

从这位巨佬博客看懂的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值