NOI Online #2 提高组 第二题:子序列问题

25 篇文章 0 订阅
11 篇文章 0 订阅

NOI Online #2 提高组 第二题:子序列问题

前言

我这道题考试时候也爆零了。

但是,注意,这个一百分题解很长那是因为很详细

题目

传送门

解析

暴力

最暴力的做法即为枚举每一对 l l l r r r,并分别计算其 f ( l , r ) f(l,r) f(l,r) 的值。但这样不知道会慢到哪里去了,所以我们考虑只滚动 l l l的值,并每次计算 G ( l ) = ∑ r = l n f 2 ( l , r ) G(l)=\sum_{r=l}^{n} f^2(l,r) G(l)=r=lnf2(l,r) 的值。

转移

考虑 G ( l ) G(l) G(l) 如何从 G ( l − 1 ) G(l-1) G(l1) 转移过来,由 G G G 的定义:

G ( l − 1 ) = f 2 ( l − 1 , l − 1 ) + f 2 ( l − 1 , l ) ⋯ + f 2 ( l − 1 , n ) G(l-1)=f^2(l-1,l-1)+f^2(l-1,l)\cdots+f^2(l-1,n) G(l1)=f2(l1,l1)+f2(l1,l)+f2(l1,n)
G ( l ) = f 2 ( l , l ) + ⋯ + f 2 ( l , n ) G(l)=f^2(l,l)+\cdots+f^2(l,n) G(l)=f2(l,l)++f2(l,n)

不难发现, G ( l ) G(l) G(l) G ( l − 1 ) G(l-1) G(l1)的唯一区别在于 G ( l ) G(l) G(l)中的每个 f ( l , i ) f(l,i) f(l,i) 的值都少考虑了 A l − 1 A_{l-1} Al1​,即:

f ( l − 1 , l − 1 ) → 0 f ( l − 1 , l ) → f ( l , l ) f ( l − 1 , l + 1 ) → f ( l , l + 1 ) f ( l − 1 , l + 2 ) → f ( l , l + 2 ) ⋮ \begin{aligned} f(l-1,l-1)&\rightarrow0\cr f(l-1,l)&\rightarrow f(l,l)\cr f(l-1,l+1)&\rightarrow f(l,l+1)\cr f(l-1,l+2)&\rightarrow f(l,l+2)\cr &\vdots \end{aligned} f(l1,l1)f(l1,l)f(l1,l+1)f(l1,l+2)0f(l,l)f(l,l+1)f(l,l+2)

f ( l − 1 , l − 1 ) f(l-1,l-1) f(l1,l1)彻底没了)。

所以为了计算 A l − 1 A_{l-1} Al1对哪些 f f f有影响,我们可以先预处理出一个数组 s u f [ i ] suf[i] suf[i],表示 A x = A i A_x=A_i Ax=Ai​ 且 x > i x>i x>i 时, x x x 的最小值(如果找不到这样的数,则 s u f [ i ] = n + 1 suf[i]=n+1 suf[i]=n+1)。

在从 G ( l − 1 ) G(l-1) G(l1) 转移到 G ( l ) G(l) G(l)的过程中, A l − 1 A_{l-1} Al1的消失只对 f ( l − 1 , l − 1 ∼ s u f [ l − 1 ] − 1 ) f(l-1,l-1\sim suf[l-1]-1) f(l1,l1suf[l1]1)有影响(会-1),因为 f ( l − 1 , s u f [ l − 1 ] ∼ n ) f(l-1,suf[l-1]\sim n) f(l1,suf[l1]n) 中,都有超过两个与 A l − 1 A_{l-1} Al1相等的元素,所以即使 A l − 1 A_{l-1} Al1消失了,这些区间内数字的种类数也不会变。

即:
f ( l , r ) = { f ( l − 1 , r ) + 1 ( r ∈ [ l − 1 , s u f [ l − 1 ] − 1 ] ) f ( l − 1 , r ) ( r ∈ [ s u f [ l − 1 ] , n ] ) f(l,r)=\left\{ \begin{aligned} &f(l-1,r)+1&&(r\in[l-1,suf[l-1]-1])\cr &f(l-1,r)&&(r\in[suf[l-1],n]) \end{aligned} \right. f(l,r)={f(l1,r)+1f(l1,r)(r[l1,suf[l1]1])(r[suf[l1],n])

这样,我们就可以通过线段树维护 f ( l − 1 , i ) f(l-1,i) f(l1,i),在 log ⁡ ( n ) \log(n) log(n)的时间内,将每一个 f ( l − 1 , i ) f(l-1,i) f(l1,i) 转移成 f ( l , i ) f(l,i) f(l,i)(区间修改)。

但是这题答案要求的是 f ( l , i ) f(l,i) f(l,i)的平方和,怎么办?

由前缀和维护平方和

对于 f ( l − 1 , r ) f(l-1,r) f(l1,r) ,如果 f ( l , r ) = f ( l − 1 , r ) − 1 f(l,r)=f(l-1,r)-1 f(l,r)=f(l1,r)1那么

f 2 ( l , r ) = ( f ( l − 1 , r ) − 1 ) 2 = f 2 ( l − 1 , r ) − ( 2 f ( l − 1 , r ) − 1 ) \begin{aligned} f^2(l,r)&=(f(l-1,r)-1)^2\cr &=f^2(l-1,r)-(2f(l-1,r)-1) \end{aligned} f2(l,r)=(f(l1,r)1)2=f2(l1,r)(2f(l1,r)1)

又因为

G ( l − 1 ) − G ( l ) = ( f 2 ( l − 1 , l − 1 ) − 0 ) + ( f 2 ( l − 1 , l ) − f 2 ( l , l ) ) + ( f 2 ( l − 1 , l + 1 ) − f 2 ( l , l + 1 ) ) + ⋮ ( f 2 ( l − 1 , n ) − f 2 ( l , n ) ) \begin{aligned} G(l-1)-G(l)=(f^2(l-1,l-1)&-0)+\cr (f^2(l-1,l)&-f^2(l,l))+\cr (f^2(l-1,l+1)&-f^2(l,l+1))+\cr &\vdots\cr (f^2(l-1,n)&-f^2(l,n)) \end{aligned} G(l1)G(l)=(f2(l1,l1)(f2(l1,l)(f2(l1,l+1)(f2(l1,n)0)+f2(l,l))+f2(l,l+1))+f2(l,n))

又因为刚才推出 r ∈ [ s u f [ l − 1 ] , n ] r\in[suf[l-1],n] r[suf[l1],n]时, f ( l − 1 , r ) f(l-1,r) f(l1,r) f ( l , r ) f(l,r) f(l,r) 相同,所以式子中许多项被消掉了(式子中的 n n n 变成了 s u f [ l − 1 ] suf[l-1] suf[l1]

G ( l − 1 ) − G ( l ) = ( f 2 ( l − 1 , l − 1 ) − 0 ) + ( f 2 ( l − 1 , l ) − f 2 ( l , l ) ) + ( f 2 ( l − 1 , l + 1 ) − f 2 ( l , l + 1 ) ) + ⋮ ( f 2 ( l − 1 , s u f [ l − 1 ] − 1 ) − f 2 ( l , s u f [ l − 1 ] − 1 ) ) \begin{aligned} G(l-1)-G(l)=(f^2(l-1,l-1)&-0)+\cr (f^2(l-1,l)&-f^2(l,l))+\cr (f^2(l-1,l+1)&-f^2(l,l+1))+\cr &\vdots\cr (f^2(l-1,{\color{red}suf[l-1]-1})&-f^2(l,{\color{red}suf[l-1]-1})) \end{aligned} G(l1)G(l)=(f2(l1,l1)(f2(l1,l)(f2(l1,l+1)(f2(l1,suf[l1]1)0)+f2(l,l))+f2(l,l+1))+f2(l,suf[l1]1))

又由于第二部分的结论( r ∈ [ l − 1 , s u f [ l − 1 ] − 1 ] r\in[l-1,suf[l-1]-1] r[l1,suf[l1]1]时, f ( l − 1 , r ) = f ( l , r ) + 1 f(l-1,r)=f(l,r)+1 f(l1,r)=f(l,r)+1)和这部分开头的结论,式子可以再次化简:

G ( l ) − G ( l − 1 ) = − ( 2 f ( l − 1 , l − 1 ) − 1 ) − ( 2 f ( l − 1 , l ) − 1 ) − ( 2 f ( l − 1 , l + 1 ) − 1 ) ⋮ − ( 2 f ( l − 1 , s u f [ l − 1 ] − 1 ) − 1 ) = − 2 × ∑ r = l − 1 n f ( l − 1 , r ) + ( ( s u f [ l − 1 ] ) − ( l − 1 ) + 1 ) \begin{aligned} G(l)-G(l-1)&=&-(2f(l-1,l-1)&-1)\cr &&-(2f(l-1,l)&-1)\cr &&-(2f(l-1,l+1)&-1)\cr &&&\vdots\cr &&-(2f(l-1,suf[l-1]-1)&-1)\cr &=&-2\times\sum_{r=l-1}^n f(l-1,r)+((suf[l-1])&-(l-1)+1) \end{aligned} G(l)G(l1)==(2f(l1,l1)(2f(l1,l)(2f(l1,l+1)(2f(l1,suf[l1]1)2×r=l1nf(l1,r)+((suf[l1])1)1)1)1)(l1)+1)

这个值可以直接用刚才维护 f f f的线段树来算出。

代码

#include<bits/stdc++.h>
using namespace std;
const long long Mod=1000000007;
const long long MXN=1e6+5;
long long n;
long long arr[MXN],pool[MXN];
long long suf[MXN],nxt[MXN],diff[MXN];
long long tot=0,ans=0;
struct set_tree{ //线段树板子
	long long t[MXN<<2],tag[MXN<<2];
	long long ls(long long p){return p<<1;}
	long long rs(long long p){return (p<<1)|1;}
	void push_up(long long p){t[p]=t[ls(p)]+t[rs(p)];}
	void add_tag(long long p,long long l,long long r,long long k){
		tag[p]+=k;
		t[p]+=k*(r-l+1);
	}
	inline void push_down(long long p,long long l,long long r){
		if(!tag[p])return;
		long long mid=(l+r)>>1;
		add_tag(ls(p),l,mid,tag[p]);
		add_tag(rs(p),mid+1,r,tag[p]);
		tag[p]=0;
	}
	void build(long long p,long long l,long long r){
		tag[p]=0;
		if(l==r){
			t[p]=diff[l];
			return;
		}
		long long mid=(l+r)>>1;
		build(ls(p),l,mid);
		build(rs(p),mid+1,r);
		push_up(p);
	}
	void mo(long long p,long long l,long long r,long long al,long long ar,long long k){
		if(al>ar)return;
		if(al<=l && r<=ar){
			add_tag(p,l,r,k);
			return;
		}
		long long mid=(l+r)>>1;
		push_down(p,l,r);
		if(al<=mid)mo(ls(p),l,mid,al,ar,k);
		if(ar>mid)mo(rs(p),mid+1,r,al,ar,k);
		push_up(p);
	}
	long long query(long long p,long long l,long long r,long long al,long long ar){
		if(al>ar)return 0;
		if(al<=l && r<=ar) return t[p];
		long long mid=(l+r)>>1;
		push_down(p,l,r);
		long long res=0;
		if(al<=mid)res+=query(ls(p),l,mid,al,ar);
		if(ar>mid)res+=query(rs(p),mid+1,r,al,ar);
		return res;
	}
}Tree;
int main()
{
	scanf("%lld",&n);
	for(long long i=1;i<=n;i++)
	{
		scanf("%lld",arr+i);
		pool[i]=arr[i];
		nxt[i]=n+1;
	}
	sort(pool+1,pool+1+n);
	long long cnt=unique(pool+1,pool+1+n)-pool-1;
	for(long long i=1;i<=n;i++)
		arr[i]=lower_bound(pool+1,pool+1+cnt,arr[i])-pool;
	for(long long i=n;i>=1;i--)
	{
		suf[i]=nxt[arr[i]];
		nxt[arr[i]]=i;
	}
	memset(pool,0,sizeof(pool));
	for(long long i=1;i<=n;i++){
		diff[i]=diff[i-1];
		diff[i]+=((pool[arr[i]]++)==0);
		tot+=diff[i]*diff[i];
		tot%=Mod;
	}
	Tree.build(1,1,n);
	for(long long i=1;i<=n;i++)
	{
		ans+=tot;
		ans%=Mod;
		tot-=(Tree.query(1,1,n,i,suf[i]-1)*2)-(suf[i]-i);
		tot%=Mod;
		Tree.mo(1,1,n,i+1,suf[i]-1,-1);
	}
	ans%=Mod;
	if(ans<0)ans+=Mod;
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值