把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P6477 [NOI Online 2 提高组]子序列问题sequence

题面传送门
考虑像HH的项链那道题一样维护最后一个位置。
如果在第\(j\)个点设\(f_i\)为当前\(i\)\(j\)的答案,那么如果没有平方,显然可以对于每一个最后一个点\(last\),将\(1\)\(last\)都加上\(1\)。查询时区间查询即可。
可现在是平方和,我们知道\((x+y)^2=x^2+2xy+y^2\)那么直接维护两个,一个是区间加,一个是区间平方和,更新时用区间加更新区间平方和即可。
代码实现:

#include<cstdio>
#include<algorithm>
#define mod 1000000007
using namespace std;
long long n,m,k,f[4000039],s[4000039],sum[4000039],a[1000039],last[1000039],x,y,z,ans,tot,pus,now[1000039],nows[1000039];
inline void read(register long long &x){
	register char s=getchar();x=0;
	while(s<'0'||s>'9') s=getchar();
	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
inline void push(register int l,register int r,register int now){
	register int m=(l+r)>>1;
	f[now<<1]+=f[now];f[now<<1|1]+=f[now];
	sum[now<<1]=s[now<<1]*f[now]*2+f[now]*f[now]*(m-l+1)+sum[now<<1];
	sum[now<<1|1]=s[now<<1|1]*f[now]*2+f[now]*f[now]*(r-m)+sum[now<<1|1];
	s[now<<1]=(m-l+1)*f[now]+s[now<<1];
	s[now<<1|1]=(r-m)*f[now]+s[now<<1|1];
	f[now]=0;
}
inline void get(register int l,register int r,register int now){
	if(x<=l&&r<=y){f[now]++,sum[now]=s[now]*2+(r-l+1)+sum[now],s[now]=s[now]+r-l+1;return;}
	if(f[now])push(l,r,now);
	register int m=(l+r)>>1;
	if(x<=m) get(l,m,now<<1);
	if(y>m) get(m+1,r,now<<1|1);
	sum[now]=sum[now<<1]+sum[now<<1|1];
	s[now]=s[now<<1]+s[now<<1|1];
}
inline long long find(register int l,register int r,register int now){
	if(x<=l&&r<=y) return sum[now];
	if(f[now])push(l,r,now);
	register int m=(l+r)>>1;
	register long long fs=0;
	if(x<=m) fs+=find(l,m,now<<1);
	if(y>m) fs+=find(m+1,r,now<<1|1);
	return fs;
}
int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	register int i,l,r,mid;
	read(n);
	for(i=1;i<=n;i++)read(a[i]),now[i]=a[i];
	sort(now+1,now+n+1);
	for(i=1;i<=n;i++){
		nows[i]=nows[i-1];
		if(now[i]!=now[i-1]) nows[i]++;
	}
	for(i=1;i<=n;i++){
		l=0;r=n+1;
		while(l+1<r){
			mid=(l+r)>>1;
			if(now[mid]<a[i]) l=mid;
			else r=mid;
		}
		a[i]=nows[r];
		x=last[a[i]]+1,y=i,z=1,get(1,n,1),last[a[i]]=i;
		x=1;y=i;
		ans=(ans+find(1,n,1))%mod;
	}
	printf("%lld\n",ans);
	return 0;
}

这道题的极限答案只有\(10^{18}\),但\(longlong\)可以达到\(9\times10^{18}\)的范围,所以不用在中间取模,珂以减小常数。

posted @ 2020-04-25 12:34  275307894a  阅读(42)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end