[CF1083D][线段树][单调栈]The Fair Nut's getting crazy

CF1083D

按照套路,我们从左往右枚举交区间的r,并计算方案数
设L[i],R[i]为i前面/后面第一个与它相同的位置,pos为最小的满足[pos,i]中没有重复数的位置,则以r为交区间的右端点的方案数为 ∑ i = p o s r ( m n { R [ i ] } − r ) ( i − m x { L [ i ] } ) \sum_{i=pos}^r(mn\{R[i]\}-r)(i-mx\{L[i]\}) i=posr(mn{R[i]}r)(imx{L[i]}),拆开用线段树维护即可,然后mx和mn就和CF997E一样用单调栈维护即可

Code:

#include<bits/stdc++.h>
#define mod 1000000007
#define ll long long
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1e5+5;
inline int add(ll x,ll y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(ll x,ll y){x-=y;if(x<0) x+=mod;return x%mod;}
inline int mul(ll x,ll y){return x*y%mod;}
inline void inc(ll &x,ll y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(ll &x,ll y){x-=y;if(x<0) x+=mod;}
inline ll S(int x){return 1ll*x*(x+1)/2;}
namespace segtree{
	struct info{
		ll smx,smn,smni,ml;
		info(){}
		info(ll _smx,ll _smn,ll _smni,ll _ml):smx(_smx),smn(_smn),smni(_smni),ml(_ml){}
		friend inline info operator + (const info &a,const info &b){return info(add(a.smx,b.smx),add(a.smn,b.smn),add(a.smni,b.smni),add(a.ml,b.ml));}
	};
	struct seg{int l,r;info t;ll tmx,tmn;}tr[N<<2];
	#define ls tr[k].l
	#define rs tr[k].r
	#define mid (ls+rs>>1)
	inline void pushup(int k){tr[k].t=tr[k<<1].t+tr[k<<1|1].t;}
	inline void pushmn(int k,int v){
		inc(tr[k].tmn,v);
		inc(tr[k].t.ml,mul(v,tr[k].t.smx));
		inc(tr[k].t.smn,mul(v,(rs-ls+1)));
		inc(tr[k].t.smni,mul(v,dec(S(rs),S(ls-1))));
	}
	inline void pushmx(int k,int v){
		inc(tr[k].tmx,v);
		inc(tr[k].t.ml,mul(v,tr[k].t.smn));
		inc(tr[k].t.smx,mul(v,(rs-ls+1)));	
	}
	inline void pushdown(int k){
		if(tr[k].tmx){
			pushmx(k<<1,tr[k].tmx);
			pushmx(k<<1|1,tr[k].tmx);
			tr[k].tmx=0;
		}
		if(tr[k].tmn){
			pushmn(k<<1,tr[k].tmn);
			pushmn(k<<1|1,tr[k].tmn);
			tr[k].tmn=0;
		}
	}
	void build(int k,int l,int r){
		ls=l;rs=r;tr[k].tmx=tr[k].tmn=0;tr[k].t=info(0,0,0,0);
		if(l==r) return;
		build(k<<1,l,mid);build(k<<1|1,mid+1,r);
	}
	void modify(int k,int ql,int qr,int v,int op){
		if(rs<ql || ls>qr) return;
		if(ql<=ls && rs<=qr) return op?pushmx(k,v):pushmn(k,v);
		pushdown(k);
		if(qr<=mid) modify(k<<1,ql,qr,v,op);
		else if(ql>mid) modify(k<<1|1,ql,qr,v,op);
		else modify(k<<1,ql,mid,v,op),modify(k<<1|1,mid+1,qr,v,op);
		pushup(k);
	}
	info query(int k,int ql,int qr){
		if(rs<ql || ls>qr) return info(0,0,0,0);
		if(ql<=ls && rs<=qr) return tr[k].t;
		pushdown(k);
		if(qr<=mid) return query(k<<1,ql,qr);
		else if(ql>mid) return query(k<<1|1,ql,qr);
		else return query(k<<1,ql,mid)+query(k<<1|1,mid+1,qr);
	}
}
using namespace segtree;
int n,cnt;
int tong[N],a[N],L[N],R[N],bnd[N];
int stmx[N],stmn[N],topmx=0,topmn=0;
ll ans=0;
int main(){
	n=read();build(1,1,n);
	for(int i=1;i<=n;i++) a[i]=bnd[i]=read();
	sort(bnd+1,bnd+n+1);
	cnt=unique(bnd+1,bnd+n+1)-bnd-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(bnd+1,bnd+cnt+1,a[i])-bnd;
	for(int i=1;i<=n;i++){
		L[i]=tong[a[i]]+1;
		tong[a[i]]=i;
	}
	for(int i=1;i<=cnt;i++) tong[i]=n+1;
	for(int i=n;i;i--){
		R[i]=tong[a[i]]-1;
		tong[a[i]]=i;
	}
	int pos=1;
	for(int i=1;i<=n;i++){
		while(topmx && L[i]>=L[stmx[topmx]]) modify(1,stmx[topmx-1]+1,stmx[topmx],dec(0,L[stmx[topmx]]),1),--topmx;
		modify(1,stmx[topmx]+1,i,L[i],1);
		stmx[++topmx]=i;
		
		while(topmn && R[i]<=R[stmn[topmn]]) modify(1,stmn[topmn-1]+1,stmn[topmn],dec(0,R[stmn[topmn]]),0),--topmn;
		modify(1,stmn[topmn]+1,i,R[i],0);
		stmn[++topmn]=i;
		
		pos=max(pos,L[i]);
		info res=query(1,pos,i);
		ll tmp=dec(add(res.smni,mul(i,res.smx)),add(res.ml,mul(dec(S(i),S(pos-1)),i)));
		inc(ans,tmp);
	}
	cout<<ans;
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值