【bzoj4540】[Hnoi2016]序列

题目链接

好久以前写的东西了==,现在已经记不起来当时线段树咋写的了
于是 Y Y YY YY出一个 C D Q CDQ CDQ的做法
好像还没人发过 C D Q CDQ CDQ的题解?抢个fb

首先先把给的序列预处理一把
L [ i ] L[i] L[i] i i i左边小等 a [ i ] a[i] a[i]的位置 + 1 +1 +1
同理有 R [ i ] R[i] R[i] i i i右边小等 a [ i ] a[i] a[i]的位置 − 1 -1 1

然后上一把二维数点叫我二维数点大师
显然可以发现,一个以 ( L [ i ] , i ) (L[i],i) (L[i],i)为左下角的,以 ( i , R [ i ] ) (i,R[i]) (i,R[i])为右上角的矩形会贡献 a [ i ] a[i] a[i]的答案(也就是说,这个矩形里全是 a [ i ] a[i] a[i]
然后询问就相当于查询一个以 ( l , l ) (l,l) (l,l)为左下角的,以 ( r , r ) (r,r) (r,r)为右上角的矩形和

考虑操作和询问都具有前缀性,于是可以把一个矩形拆成四个点
具体来说,把操作 ( x l , x r , y l , y r ) (xl,xr,yl,yr) (xl,xr,yl,yr)拆成 ( x l , y l ) , ( x l , y r + 1 ) , ( x r + 1 , y l ) , ( x r + 1 , y r + 1 ) (xl,yl),(xl,yr + 1),(xr + 1,yl),(xr + 1,yr + 1) (xl,yl),(xl,yr+1),(xr+1,yl),(xr+1,yr+1)
把询问 ( x l , x r , y l , y r ) (xl,xr,yl,yr) (xl,xr,yl,yr)拆成 ( x r , y r ) , ( x l − 1 , y r ) , ( x r , y l − 1 ) , ( x l − 1 , y l − 1 ) (xr,yr),(xl - 1,yr),(xr,yl - 1),(xl - 1,yl - 1) (xr,yr),(xl1,yr),(xr,yl1),(xl1,yl1)
然后填填正负号
答案即为询问和操作的四个点两两贡献的和

当然直接计算两两贡献是 O ( n 2 ) O(n ^ 2) O(n2)
考虑 C D Q CDQ CDQ分治

就是先把询问和操作都按 x x x排序,然后考虑计算完贡献后归并 y y y上来

然后考虑一件事情:如何计算贡献?
如果说直接计算贡献的话,就是 v a l [ i ] ∗ v a l [ j ] . v a l ∗ a b s ( x [ i ] − x [ j ] + 1 ) ∗ a b s ( y [ i ] − y [ j ] + 1 ) val[i] * val[j].val * abs(x[i] - x[j] + 1) * abs(y[i] - y[j] + 1) val[i]val[j].valabs(x[i]x[j]+1)abs(y[i]y[j]+1)(也就是把以 i i i j j j形成的矩形大小乘以权值,其中 i i i为操作, j j j为询问, v a l val val是填上正负号的权值)
当然这样直接做是不行的,考虑怎么利用 x [ i ] &lt; x [ j ] x[i] &lt; x[j] x[i]<x[j]并且 y y y有序的性质

考虑某个 j j j点计算完答案长啥样子:
在这里插入图片描述
然后我们现在要把他变成
在这里插入图片描述
类似这样的形状

记一个 l e n len len w i d wid wid,分别表示 i 1 , i 2 , i 3 i_1,i_2,i_3 i1,i2,i3 j j j的长和宽的带权和
具体而言就是
在这里插入图片描述
(大概是这个意思,只不过要带权
那我们每次先把原来的矩形向左向右扩一下,再向上向下扩一下,就可以了
l e n len len w i d wid wid表现出来就是
n o w + = w i d ∗ ( x [ i ] − x [ j − 1 ] ) ; l e n + = s u m ∗ ( x [ j ] − x [ j − 1 ] ) ; now += wid * (x[i] - x[j - 1]); len += sum * (x[j] - x[j - 1]); now+=wid(x[i]x[j1]);len+=sum(x[j]x[j1]);
n o w + = l e n ∗ ( y [ i ] − y [ j − 1 ] ) ; w i d + = s u m ∗ ( y [ i ] − y [ j − 1 ] ) ; now += len * (y[i] - y[j - 1]); wid += sum * (y[i] - y[j - 1]); now+=len(y[i]y[j1]);wid+=sum(y[i]y[j1]);
s u m sum sum表示当前所有点的权值和
当然可能会出现新的点(也就是原来不在 j j j的范围内,现在出现在 j + 1 j + 1 j+1的范围内了),那么这个直接加起来就好了

最后要开 _ i n t 128 \_int128 _int128,当然因为博主比较懒, l o n g   l o n g long\ long long long交了一个 80 p t s 80pts 80pts的就没再管了

代码( 80 p t s 80pts 80pts

#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long LL;

const int maxn = 1000010;

struct node{
	int x,y,val,typ,id;
}q[maxn],tmp[maxn];

int n,m,N,L[maxn],R[maxn],a[maxn];
int stk[maxn],top;
LL ans[maxn];

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline int cmp(node a,node b)
{
	return a.x < b.x || (a.x == b.x && a.y < b.y) || (a.x == b.x && a.y == b.y && a.typ < b.typ);
}

inline void solve(int l,int r)
{
	if (l == r) return;
	int mid = l + r >> 1;
	solve(l,mid); solve(mid + 1,r);
	int j = l,pre = 0; LL now = 0,len = 0,wid = 0,cnt = 0;
	for (int i = mid + 1; i <= r; i++)
	{
		if (!q[i].typ) continue;
		now += wid * (q[i].x - q[pre].x); len += 1ll * cnt * (q[i].x - q[pre].x);
		now += len * (q[i].y - q[pre].y); wid += 1ll * cnt * (q[i].y - q[pre].y);
		pre = i;
		while (j <= mid && q[j].y <= q[i].y)
		{
			if (q[j].typ) {j++; continue;}
			len += 1ll * q[j].val * (q[i].x - q[j].x + 1); 
			wid += 1ll * q[j].val * (q[i].y - q[j].y + 1);
			now += 1ll * q[j].val * (q[i].x - q[j].x + 1) * (q[i].y - q[j].y + 1);
			cnt += q[j].val; j++;
		}
		ans[q[i].id] += q[i].val * now;
	}

	int u = l,v = mid + 1,cur = l - 1;
	while (u <= mid && v <= r)
	{
		if (q[u].y <= q[v].y) tmp[++cur] = q[u++];
		else tmp[++cur] = q[v++];
	}
	for (int i = u; i <= mid; i++) tmp[++cur] = q[i];
	for (int i = v; i <= r; i++) tmp[++cur] = q[i];
	for (int i = l; i <= r; i++) q[i] = tmp[i];
}

inline void split(int xl,int xr,int yl,int yr,int typ,int id)
{
	if (typ == 0)
	{
		q[++N] = (node){xl,yl,a[id],0,id};
		q[++N] = (node){xl,yr + 1,-a[id],0,id};
		q[++N] = (node){xr + 1,yl,-a[id],0,id};
		q[++N] = (node){xr + 1,yr + 1,a[id],0,id};
	}
	else 
	{
		q[++N] = (node){xr,yr,1,1,id};
		q[++N] = (node){xl - 1,yr,-1,1,id};
		q[++N] = (node){xr,yl - 1,-1,1,id};
		q[++N] = (node){xl - 1,yl - 1,1,1,id};
	}
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();
	for (int i = 1; i <= n; i++) a[i] = getint();
	for (int i = 1; i <= n; i++)
	{
		while (top && a[stk[top]] > a[i]) top--;
		L[i] = stk[top] + 1;
		stk[++top] = i;
	}

	top = 0;
	for (int i = n; i >= 1; i--)
	{
		while (top && a[stk[top]] > a[i]) top--;
		if (top) R[i] = stk[top] - 1; else R[i] = n;
		stk[++top] = i;
	}

	for (int i = 1; i <= n; i++)
		split(L[i],i,i,R[i],0,i);
	
	for (int i = 1; i <= m; i++)
	{
		int l = getint(),r = getint();
		split(l,r,l,r,1,i);
	}

	sort(q + 1,q + N + 1,cmp);

	solve(1,N);

	for (int i = 1; i <= m; i++)
		printf("%lld\n",ans[i]);
	return 0;
}

非常好写并且短233333333

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值