朴素的做法是枚舉左、右端點。用\(\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,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;
}