noi模拟赛#2 T2 sequence

D e s c r i p t i o n Description Description

给定一个长度为 n n n的序列 A A A

定义 f ( l , r ) f(l,r) f(l,r)表示序列 [ l , r ] [l,r] [l,r]中不同的数的个数

强令 l ≤ r l\leq r lr,求所有 f ( l , r ) 2 f(l,r)^2 f(l,r)2

数据范围: n ≤ 1 0 6 , A i ≤ 1 0 9 n\leq 10^6,A_i\leq 10^9 n106,Ai109


S o l u t i o n Solution Solution

看到 A i A_i Ai的数据范围,首先肯定是要离散化的

问题如果简单点,如果没有那个平方,我们如何求?

显然,我们可以记录每个数 a i a_i ai上次出现的位置 p r e i pre_i prei,然后对于每个数 A i A_i Ai,我们让 T T T数组的 [ p r e i + 1 , i ] [pre_i+1,i] [prei+1,i]这段区间加1,此时 T x T_x Tx表示的就是 [ x , i ] [x,i] [x,i]的不同数的个数

自然到 n n n时, T i T_i Ti表示的就是 [ i , n ] [i,n] [i,n]的不同数的个数(后缀)

那么, T i = f ( i , n ) T_i=f(i,n) Ti=f(i,n)

A n s = ∑ i = 1 n ( T i ) 2 Ans=\sum_{i=1}^n (T_i)^2 Ans=i=1n(Ti)2

此时,我们显然只需要一种支持区间修改,区间查询平方和的数据结构

我选择了线段树

由于作者本人是菜鸡,平方和的维护过程是比赛时手推的

自然,我们都知道维护和,它自然等于左右子树的和
显然平方和也是左右子树的和,这个毋庸置疑
恶心的是我们该如何维护 l z y lzy lzy呢?

赛时推导草稿

( x + y ) 2 = x 2 + 2 x y + y 2 (x+y)^2=x^2+2xy+y^2 (x+y)2=x2+2xy+y2——完全平方公式
a , b , c , d a,b,c,d a,b,c,d——假如有四个数,我们已知它们的和
a 2 , b 2 , c 2 , d 2 a^2,b^2,c^2,d^2 a2,b2,c2,d2——以及它们的平方和
( a + p ) 2 , ( b + p ) 2 , ( c + p ) 2 , ( d + p ) 2 (a+p)^2,(b+p)^2,(c+p)^2,(d+p)^2 (a+p)2,(b+p)2,(c+p)2,(d+p)2如果它们同时加上 p p p
则在原有的基础上加上—— 2 p a + p 2 + 2 p b + p 2 + 2 p c + p 2 + 2 p d + p 2 2pa+p^2+2pb+p^2+2pc+p^2+2pd+p^2 2pa+p2+2pb+p2+2pc+p2+2pd+p2
= 2 p ( a + b + c + d ) + 4 p 2 2p(a+b+c+d)+4p^2 2p(a+b+c+d)+4p2
拓展到 n n n个数同理

也就是说
我们已知 n n n个数的和—— t o t tot tot
以及 n n n个数的平方和—— s u m sum sum

假如每个数加上 p p p,则 t o t tot tot需要加上 n × p n\times p n×p
s u m sum sum需要加上 2 p × t o t + n p 2 2p\times tot+np^2 2p×tot+np2

由于 s u m sum sum的加用到了 t o t tot tot,所以应该先维护 s u m sum sum,再维护 t o t tot tot

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)


C o d e Code Code

#include
#include
#include
#define LL long long
#define N 1000010
#define mod 1000000007
using namespace std;int n,a[N],b[N],m,sum,t[N],pre[N];
LL ans;
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
struct xds
{
	#define lson k<<1
	#define rson k<<1|1
	LL tot[N<<2],sum[N<<2],lzy[N<<2];
	inline void pushup(int k)//上传
	{
		tot[k]=tot[lson]+tot[rson];
		sum[k]=sum[lson]+sum[rson];
		return;
	}
	inline void pushdown(int k,int l,int r)
	{
		if(lzy[k]==0) return;
		int mid=l+r>>1,lenl=mid-l+1,lenr=r-mid;
		(sum[lson]+=2*lzy[k]*tot[lson]%mod+lenl*lzy[k]%mod*lzy[k]%mod)%=mod;//维护左区间
		(sum[rson]+=2*lzy[k]*tot[rson]%mod+lenr*lzy[k]%mod*lzy[k]%mod)%=mod;//维护右区间
		(tot[lson]+=lenl*lzy[k]%mod)%=mod;
		(tot[rson]+=lenr*lzy[k]%mod)%=mod;
		lzy[lson]+=lzy[k];lzy[rson]+=lzy[k];
		lzy[k]=0;
		return;
	}
	inline void modify(int ql,int qr,int k=1,int l=1,int r=n)
	{
		if(ql<=l&&r<=qr)
		{
			(sum[k]+=2*tot[k]%mod+(r-l+1))%=mod;//每个位置都加1,1的平方还是1
			tot[k]+=(r-l+1);//每个位置都加1,总共加这么多
			lzy[k]++;
			return;
		}
		pushdown(k,l,r);
		int mid=l+r>>1;
		if(ql<=mid) modify(ql,qr,lson,l,mid);
		if(qr>mid) modify(ql,qr,rson,mid+1,r);
		pushup(k);
		return;
	}
	inline LL quary(int ql,int qr,int k=1,int l=1,int r=n)
	{
		if(ql<=l&&r<=qr) return sum[k];
		int mid=l+r>>1;
		LL res=0;
		pushdown(k,l,r);
		if(ql<=mid) res+=quary(ql,qr,lson,l,mid);
		if(qr>mid) res+=quary(ql,qr,rson,mid+1,r);
		pushup(k);
		return res%mod;
	}
}T;
signed main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	n=read();
	for(register int i=1;i<=n;i++) a[i]=b[i]=read();
	sort(a+1,a+1+n);
	m=unique(a+1,a+1+n)-a-1;
	for(register int i=1;i<=n;i++) b[i]=lower_bound(a+1,a+1+m,b[i])-a;
	for(register int i=1;i<=n;i++)
	{
		if(t[b[i]]) pre[i]=t[b[i]];
		t[b[i]]=i;
		T.modify(pre[i]+1,i);
		ans+=T.quary(1,n);ans%=mod;
	}
	printf("%lld",ans);
	fclose(stdin);
	fclose(stdout);
}

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