[USACO 2020.1 Platinum][LOJ3247]Non-Decreasing Subsequences(CDQ分治)


題面

https://loj.ac/problem/3247

題解

考慮CDQ分治。對於\(solve(l,r,v)\)(其中l,r表示當前處理到的區間的左右端點,v是一個vector,存放當前區間內待處理的所有詢問的序號):

  • \(m=(l+r)>>1\)

  • 預處理出\(f[i][j](1{\leq}i{\leq}k,l{\leq}j{\leq}r)\),其中\(f[i][j]=\)

    \[\begin{cases} {以j為左端點,右端點{\leq}m,且a[右端點]=i的不下降子序列數}(l{\leq}j{\leq}m) \\ {以j為右端點,左端點{>m,且a[左端點]=i的不下降子序列數(m<j{\leq}r)}} \end{cases} \]

    對於\(m<j{\leq}r\)來言,有遞推式

    \[f[i][j]={\sum\limits_{m<k<j,a[k]{\leq}a[j]}}f[i][k]+[a[j]=i]$$,這一過程可以用樹狀數組優化,從而在$O((r-l+1)klogk)$時間內完成預處理。 另外,需要將空字符串統計進去,就是把$f[1][m],f[k][m+1]$都加1。 接下來,對$f$進行前綴和,就可以得到$f[i][j]=$ $$\begin{cases} {l{\leq}左端點,右端點{\leq}m,且a[右端點]=i的不下降子序列數}(l{\leq}j{\leq}m) \\ {j{\geq}右端點,左端點{>m,且a[左端點]=i的不下降子序列數(m<j{\leq}r)}} \end{cases}\]

  • 遍歷所有序號\(id{\in}v\)。如果\(R[id]{\leq}m\),將其加入數組vl。如果\(L[id]>m\),將其加入數組vr。

  • 對於其余id,統計答案:

    \[{\sum\limits_{i=1}^{k}}f[i][L[id]]{\sum\limits_{j=i}^{k}}f[j][R[id]]$$。這一過程可以先計算前綴和來優化。 \]

依此過程,solve中預處理的總復雜度是\(O(nklognlogk)\),統計答案的總復雜度是\(O(qk)\)

代碼

#include<bits/stdc++.h>

using namespace std;

#define N 50000
#define Q 200000
#define ll long long
#define mod 1000000007
#define rg register
 
namespace ModCalc{
	inline void Inc(ll &x,ll y){
		x += y;if(x >= mod)x -= mod;
	}
	
	inline void Dec(ll &x,ll y){
		x -= y;if(x < 0)x += mod;
	}
	
	inline ll Add(ll x,ll y){
		Inc(x,y);return x;
	}
	
	inline ll Sub(ll x,ll y){
		Dec(x,y);return x;
	}
}
using namespace ModCalc;
 
inline ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

inline void write(ll x){
	if(x < 0)putchar('-'),x = -x;
	if(x > 9)write(x / 10);
	putchar('0' + x % 10);
}

ll n,q,k;
ll f[20+5][N+5],a[N+5],L[Q+5],R[Q+5];
ll ans[Q+5];
ll s[20+5];

struct BIT{
	ll b[20+5];
	
	inline ll lowbit(ll x){
		return x & -x;
	}
	
	inline void reset(){
		memset(b,0,sizeof(b));
	}
	
	inline void ud(ll x,ll dx){
		for(rg ll i = x;i <= k;i += lowbit(i))Inc(b[i],dx);
	}
	
	inline ll query(ll x){
		ll rt = 0;
		for(rg ll i = x;i;i -= lowbit(i))Inc(rt,b[i]);	
		return rt;
	}
}B;

inline void solve(ll l,ll r,vector<ll>v){
	if(l == r){
		for(rg ll i = 0;i < v.size();i++)ans[v[i]] = 2;
		return;
	}
	ll m = (l + r) >> 1;
	for(rg ll st = 1;st <= k;st++){
		B.reset();		
		for(rg ll i = m + 1;i <= r;i++){
			f[st][i] = B.query(a[i]);
			if(a[i] == st)Inc(f[st][i],1);
			B.ud(a[i],f[st][i]); 
		}
		for(rg ll i = m + 2;i <= r;i++)Inc(f[st][i],f[st][i-1]);
	}
	for(rg ll st = 1;st <= k;st++){
		B.reset();
		for(rg ll i = m;i >= l;i--){
			f[st][i] = B.query(k + 1 - a[i]);
			if(a[i] == st)Inc(f[st][i],1);
			B.ud(k + 1 - a[i],f[st][i]);
		}
		for(rg ll i = m - 1;i >= l;i--)Inc(f[st][i],f[st][i+1]);
	}
	for(rg ll i = l;i <= m;i++)Inc(f[1][i],1); //左半部分為空子串
	for(rg ll i = m + 1;i <= r;i++)Inc(f[k][i],1); //右半部分為空子串 
	vector<ll>vl,vr;
	vl.resize(0);
	vr.resize(0);
	for(rg ll i = 0;i < v.size();i++){
		ll id = v[i];
		if(R[id] <= m)vl.push_back(id);
		else if(L[id] > m)vr.push_back(id);
		else{
			for(rg ll j = k;j >= 1;j--)s[j] = Add(s[j+1],f[j][R[id]]);
			for(rg ll j = 1;j <= k;j++)Inc(ans[id],f[j][L[id]] * s[j] % mod);
		}
	}
	if(vl.size())solve(l,m,vl);
	if(vr.size())solve(m+1,r,vr); 
}

vector<ll>v;

int main(){
	n = read(),k = read();
	for(rg ll i = 1;i <= n;i++)a[i] = read();
	q = read();
	for(rg ll i = 1;i <= q;i++)L[i] = read(),R[i] = read();
	v.resize(0);
	for(rg ll i = 1;i <= q;i++)v.push_back(i);
	solve(1,n,v);
	for(rg ll i = 1;i <= q;i++)write(ans[i]),putchar('\n');	
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM