[NOI Online #2 提高组]子序列问题

题目链接:[NOI Online #2 提高组]子序列问题


显然我们枚举右端点,那么当前加入的右端点的值,对前面所有左端点的贡献值就是[L+1,R]

R就是当前右端点,L就是上一个相同值的位置。

所以维护区间加,区间平方和即可。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
#define int long long
#define mid (l+r>>1)
using namespace std;
const int N=1e6+10,mod=1e9+7;
int n,a[N],s2[N<<2],s1[N<<2],lazy[N<<2],res;	unordered_map<int,int> mp;
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){
    int x=0,f=1; char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
inline void push_down(int p,int l,int r){
	if(!lazy[p])	return ;
	s2[p<<1]=(s2[p<<1]+(mid-l+1)*lazy[p]%mod*lazy[p]%mod+s1[p<<1]*2LL*lazy[p]%mod)%mod;
	s1[p<<1]=(s1[p<<1]+(mid-l+1)*lazy[p]%mod)%mod;
	s2[p<<1|1]=(s2[p<<1|1]+(r-mid)*lazy[p]%mod*lazy[p]%mod+s1[p<<1|1]*2LL*lazy[p]%mod)%mod;
	s1[p<<1|1]=(s1[p<<1|1]+(r-mid)*lazy[p]%mod)%mod;
	lazy[p<<1]=(lazy[p<<1]+lazy[p])%mod;
	lazy[p<<1|1]=(lazy[p<<1|1]+lazy[p])%mod;
	lazy[p]=0;
}
void change(int p,int l,int r,int ql,int qr){
	if(l==ql&&r==qr){
		s2[p]=(s2[p]+(r-l+1)+s1[p]*2)%mod; s1[p]=(s1[p]+(r-l+1))%mod;
		lazy[p]=(lazy[p]+1)%mod;	return ;
	}
	push_down(p,l,r);
	if(qr<=mid)	change(p<<1,l,mid,ql,qr);
	else if(ql>mid)	change(p<<1|1,mid+1,r,ql,qr);
	else change(p<<1,l,mid,ql,mid),change(p<<1|1,mid+1,r,mid+1,qr);
	s1[p]=(s1[p<<1]+s1[p<<1|1])%mod;
	s2[p]=(s2[p<<1]+s2[p<<1|1])%mod;
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++)	a[i]=read();
	for(int i=1;i<=n;i++){
		change(1,1,n,mp[a[i]]+1,i);	mp[a[i]]=i;
		res=(res+s2[1])%mod;
	}
	cout<<res;
	return 0;
}

你可能感兴趣的:(线段树)