題解 洛谷P6477 [NOI Online #2 提高組] 子序列問題


題目鏈接

朴素的做法是枚舉左、右端點。用\(\texttt{set}\)維護區間內不同值的數量,時間復雜度\(O(n^2\log n)\)

考慮優化這個做法,就必須避免枚舉左、右端點。一種想法是,枚舉\(f(l,r)\)的值,然后計算這個值的出現次數。這是經典的算貢獻的思想,但是似乎無法快速求出一個\(f(l,r)\)的值在多少區間里出現過。

直接算\(f\)值的貢獻行不通,轉而考慮計算序列里每個位置對答案的貢獻。可以認為,一個數能貢獻到\(f(l,r)\)中,當且僅當它是該值在\([l,r]\)區間里第一次出現。我們記錄下序列里每個位置上的數上一次出現的位置,記為\(\text{pre}[i]\)。特別地,如果該位置上的數是在序列里第一次出現,則\(\text{pre}[i]=0\)。這樣,位置\(i\)上的數,會貢獻到\(l\in(pre[i],i], r\in[i,n]\)的這些區間的\(f\)值中。

但是,\(f\)值並不是直接累加到答案里,而是要先平方,再累加到答案。如何處理這個平方呢?我們知道,\(f(l,r)\)相當於區間里每個值第一次出現的這些位置。而\((f(l,r))^2\),就相當於在這些位置中,任選出兩個位置(可以重復)的方案數!

我們枚舉,任選出的這兩個位置,設它們分別為\((i,j)\)(不妨設\((i\leq j)\))。則一對\((i,j)\)會對多少個區間產生貢獻?不難發現,數量是:

\[(i-\max(\text{pre}[i],\text{pre}[j]))\cdot (n-j+1)\quad (i>\text{pre}[j]) \]

分別是可選擇的左端點的數量,右端點的數量。

暴力枚舉\((i,j)\),按上述式子計算,復雜度為\(O(n^2)\)

但這個式子是很好優化的。我們可以枚舉\(j\)。問題轉化為,如何快速求出:\(\sum_{i=1}^{j}\max(\text{pre}[i],\text{pre}[j])\)。我們用一個變量,記錄\(j\)前面所有\(\text{pre}[i]\)之和,則只需要把\(\leq\text{pre}[j]\)的這部分值減去,再加上相同數量的\(\text{pre}[j]\)即可。可以用兩個樹狀數組,都以\(\text{pre}\)值為下標,分別維護\(\text{pre}\)值小於\(x\)\(\text{pre}\)值之和,及其數量。

時間復雜度\(O(n\log n)\)

參考代碼:

//problem:sequence
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

const int MAXN=1e6,MOD=1e9+7;
inline int mod1(int x){return x<MOD?x:x-MOD;}
inline int mod2(int x){return x<0?x+MOD:x;}
inline void add(int& x,int y){x=mod1(x+y);}
inline void sub(int& x,int y){x=mod2(x-y);}
inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}

int n,a[MAXN+5],vals[MAXN+5],cnt_v,pre[MAXN+5],pos[MAXN+5],tosub[MAXN+5];
vector<int>vec[MAXN+5];

struct FenwickTree{
	int sum[MAXN+5],num[MAXN+5];
	void point_add(int p,int ds,int dn){
		p++;
		for(;p<=n;p+=(p&(-p)))add(sum[p],ds),add(num[p],dn);
	}
	pii prefix_query(int p){
		p++;
		int rs=0,rn=0;
		for(;p;p-=(p&(-p)))add(rs,sum[p]),add(rn,num[p]);
		return mk(rs,rn);
	}
	FenwickTree(){}
}T;

int main() {
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;++i)cin>>a[i],vals[i]=a[i];
	sort(vals+1,vals+n+1);
	cnt_v=unique(vals+1,vals+n+1)-(vals+1);
	for(int i=1;i<=n;++i){
		a[i]=lob(vals+1,vals+cnt_v+1,a[i])-vals;
	}
	for(int i=1;i<=n;++i){
		pre[i]=pos[a[i]];
		pos[a[i]]=i;
		vec[pre[i]].pb(i);
	}
	int ans=0;
	/*
	for(int i=1;i<=n;++i){
		for(int j=pre[i]+1;j<=i;++j){
			add(ans,(ll)(j-max(pre[i],pre[j]))*(n-i+1)%MOD*(i==j?1:2)%MOD);
		}
	}
	*/
	for(int i=1,sumpre=0;i<=n;++i){
		if(i>1){
			int x=(ll)i*(i-1)/2%MOD;
			sub(x,sumpre);
			pii q=T.prefix_query(pre[i]);
			add(x,q.fi);
			sub(x,(ll)q.se*pre[i]%MOD);
			x=(ll)x*(n-i+1)*2%MOD;
			sub(x,tosub[i]);
			add(ans,x);
		}
		add(ans,(ll)(i-pre[i])*(n-i+1)%MOD);
		
		add(sumpre,pre[i]);
		T.point_add(pre[i],pre[i],1);
		
		for(int j=0;j<SZ(vec[i]);++j){
			int ii=vec[i][j];
			int x=(ll)i*(i+1)/2%MOD;
			sub(x,sumpre);
			pii q=T.prefix_query(pre[ii]);
			add(x,q.fi);
			sub(x,(ll)q.se*pre[ii]%MOD);
			x=(ll)x*(n-ii+1)*2%MOD;
			add(tosub[ii],x);
		}
	}
	cout<<ans<<endl;
	return 0;
}


免責聲明!

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



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