1. 程式人生 > >【BZOJ4262】Sum 單調棧+線段樹

【BZOJ4262】Sum 單調棧+線段樹

技術分享 strong lin getchar() 上一個 highlight 線段樹 put ron

【BZOJ4262】Sum

Description

技術分享

Input

第一行一個數 t,表示詢問組數。 第一行一個數 t,表示詢問組數。 接下來 t 行,每行四個數 l_1, r_1, l_2, r_2。

Output

一共 t 行,每行一個數 Sum。

Sample Input

4
1 3 5 7
2 4 6 8
1 1 9 9
9 9 1 1

Sample Output

9322587654
9025304064
1065645568
0

HINT

1<=t<=40000,1<=L1<R1<=10^5,1<=L2<=R2<=10^5

題解:我們分開考慮max和pre的情況。我們將max(i...j)視為二維平面上點(i,j)的權值,處理出每個數左邊第一個比它大的數,然後這個數的貢獻區間可以就看成一個矩形(或三角形),而詢問就變成了求平面上一個矩形區域的權值和。可以用線段樹來搞。

不過線段樹維護歷史總和還真是不容易,打標記的部分還是好好說說吧。

維護三個值:v代表當前的區間和,s代表歷史的v之和,l代表區間長度。
維護四個標記:a,b,c,d,代表標記生效後,v=a*v+b*l,s=s+c*v+d*l。

關鍵在於標記如何合並。假如我們要將x和y的標記合並成z。

a:顯然z.a=x.a*y.a即可。
b:先要*=y.a,還要+=y.b。

c:+=x.a*y.c。
d:先要+=y.d,還要+=x.b*y.c。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=100010;
typedef long long ll;
struct Tag
{
	ll a,b,c,d;
	Tag () {a=1,b=c=d=0;}
	Tag (ll A,ll B,ll C,ll D) {a=A,b=B,c=C,d=D;}
	Tag operator + (const Tag &x) const {return Tag(a*x.a,b*x.a+x.b,a*x.c+c,d+b*x.c+x.d);}
};
struct node
{
	ll v,s,l;
	Tag t;
	node () {v=s=l=0,t=Tag();}
	node (ll a,ll b,ll c,Tag d) {v=a,s=b,l=c,t=d;}
	inline void add(Tag x)
	{
		s=s+v*x.c+l*x.d,v=v*x.a+l*x.b,t=t+x;
	}
	node operator + (const node &a) const
	{
		return node(v+a.v,s+a.s,l+a.l,Tag());
	}
}s[maxn<<2];
int m,n,top;
ll ans[maxn],v[maxn];
int st[maxn],pre[maxn];
struct QUERY
{
	int x,l,r,org,k;
}q[maxn];
bool cmp(const QUERY &a,const QUERY &b)
{
	return a.x<b.x;
}
inline void pushdown(int x)
{
	if(s[x].t.a!=1||s[x].t.b||s[x].t.c||s[x].t.d)	s[lson].add(s[x].t),s[rson].add(s[x].t),s[x].t=Tag();
}
void build(int l,int r,int x)
{
	if(l==r)
	{
		s[x]=node(),s[x].l=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,lson),build(mid+1,r,rson);
	s[x]=s[lson]+s[rson];
}
void updata(int l,int r,int x,int a,int b,Tag t)
{
	if(a>b)	return ;
	if(a<=l&&r<=b)
	{
		s[x].add(t);
		return ;
	}
	pushdown(x);
	int mid=(l+r)>>1;
	if(a<=mid)	updata(l,mid,lson,a,b,t);
	if(b>mid)	updata(mid+1,r,rson,a,b,t);
	s[x]=s[lson]+s[rson];
}
node query(int l,int r,int x,int a,int b)
{
	if(a<=l&&r<=b)	return s[x];
	pushdown(x);
	int mid=(l+r)>>1;
	if(b<=mid)	return query(l,mid,lson,a,b);
	if(a>mid)	return query(mid+1,r,rson,a,b);
	return query(l,mid,lson,a,b)+query(mid+1,r,rson,a,b);
}
void work(ll flag)
{
	int i,j;
	build(1,n,1);
	for(j=1;j<=2*m&&!q[j].x;j++);
	for(i=1;i<=n;i++)
	{
		updata(1,n,1,pre[i],i,Tag(0,v[i],0,0)),s[1].add(Tag(1,0,1,0));
		for(;j<=2*m&&q[j].x==i;j++)	ans[q[j].org]+=flag*q[j].k*query(1,n,1,q[j].l,q[j].r).s;
	}
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	m=rd();
	int i;
	ll t1=1,t2=1;
	for(i=1;i<=m;i++)	q[i].l=q[i+m].l=rd(),q[i].r=q[i+m].r=rd(),q[i].x=rd()-1,q[i+m].x=rd(),
		n=max(n,q[i+m].x),q[i].k=-1,q[i+m].k=1,q[i].org=q[i+m].org=i;
	for(i=1;i<=n;i++)	t1=t1*1023%1000000000,t2=t2*1025%1000000000,v[i]=t1^t2;
	sort(q+1,q+2*m+1,cmp);
	for(i=1;i<=n;i++)
	{
		while(top&&v[st[top]]>=v[i])	top--;
		pre[i]=st[top]+1,st[++top]=i;
	}
	work(-1);
	for(top=0,i=1;i<=n;i++)
	{
		while(top&&v[st[top]]<=v[i])	top--;
		pre[i]=st[top]+1,st[++top]=i;
	}
	work(1);
	for(i=1;i<=m;i++)	printf("%lld\n",ans[i]);
	return 0;
}

【BZOJ4262】Sum 單調棧+線段樹