【BZOJ2989】数列(CDQ分治,扫描线)

【BZOJ2989】数列(CDQ分治)

题面

BZOJ
权、。、。、权限题。。

题解

Description

给定一个长度为n的正整数数列a[i]。
定义2个位置的graze值为两者位置差与数值差的和,即graze(x,y)=|x-y|+|a[x]-a[y]|。
2种操作(k都是正整数):
1.Modify x k:将第x个数的值修改为k。
2.Query x k:询问有几个i满足graze(x,i)<=k。因为可持久化数据结构的流行,询问不仅要考虑当前数列,还要
考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的a[x]的graze值<=k的对数。(某位置多次修改为
同样的数值,按多次统计)

Input

第1行两个整数n,q。分别表示数列长度和操作数。
第2行n个正整数,代表初始数列。
第3--q+2行每行一个操作。

Output

对于每次询问操作,输出一个非负整数表示答案

Sample Input

3 5
2 4 3
Query 2 2
Modify 1 3
Query 2 2
Modify 1 2
Query 1 1

Sample Output

2
3
3

HINT

N<=60000 修改操作数<=40000 询问<=50000 Max{a[i]}含修改<=100000

题解

很容易想到把数列上的每一个点看成平面上的一个点,
那么题目中给定的限制变成了求距离一个点的曼哈顿距离在一个范围内的点。
很明显,这个区域在平面上是一个菱形,旋转\(45°\)之后就成为了一个正方形。
对于一个点\((x,y)\),旋转\(45°\)的方法是变成\((x+y,x-y)\)
那么询问曼哈顿距离不超过\(k\)就变成了询问矩形\(([x+y-k,x+y+k],[x-y-k,x-y+k])\)内的点数。
那么,问题变成了,加入一个点,查询一个矩形内的点数,
直接\(CDQ\)分治,然后扫描线统计答案就好了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 500100
#define py 250050
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int c[MAX];
int lb(int x){return x&(-x);}
void modify(int x,int w){while(x<MAX)c[x]+=w,x+=lb(x);}
int getsum(int x){int ret=0;while(x)ret+=c[x],x-=lb(x);return ret;}
int getsum(int l,int r){return getsum(r)-getsum(l-1);}
int n,Q,a[MAX],ans[MAX];
char ch[10];
struct Opt{int opt,x,y,d,id;}p[MAX],tmp[MAX];
bool operator<(Opt a,Opt b){if(a.x!=b.x)return a.x<b.x;return a.opt>b.opt;}
int tot,cnt;
void CDQ(int l,int r)
{
	if(l==r)return;int mid=(l+r)>>1,t=0;
	for(int i=l;i<=mid;++i)if(!p[i].opt)tmp[++t]=p[i];
	for(int i=mid+1;i<=r;++i)
	{
		if(!p[i].opt)continue;
		tmp[++t]=(Opt){+1,p[i].x-p[i].d,p[i].y,p[i].d,p[i].id};
		tmp[++t]=(Opt){-1,p[i].x+p[i].d,p[i].y,p[i].d,p[i].id};
	}
	sort(&tmp[1],&tmp[t+1]);
	for(int i=1;i<=t;++i)
		if(tmp[i].opt==0)modify(tmp[i].y,1);
		else ans[tmp[i].id]-=tmp[i].opt*getsum(tmp[i].y-tmp[i].d,tmp[i].y+tmp[i].d);
	for(int i=1;i<=t;++i)if(tmp[i].opt==0)modify(tmp[i].y,-1);
	CDQ(l,mid);CDQ(mid+1,r);
}
int main()
{
	n=read();Q=read();
	for(int i=1;i<=n;++i)a[i]=read(),p[++tot]=(Opt){0,i+a[i]+py,i-a[i]+py,0,0};
	for(int i=1;i<=Q;++i)
	{
		scanf("%s",ch);
		if(ch[0]=='Q')
		{
			int x=read();
			p[++tot]=(Opt){1,x+a[x]+py,x-a[x]+py,read(),++cnt};
		}
		else
		{
			int x=read();a[x]=read();
			p[++tot]=(Opt){0,x+a[x]+py,x-a[x]+py,0,0};
		}
	}
	CDQ(1,tot);
	for(int i=1;i<=cnt;++i)printf("%d\n",ans[i]);
	return 0;
}

posted @ 2018-08-12 20:36  小蒟蒻yyb  阅读(428)  评论(0编辑  收藏  举报