BZOJ2716: [Violet 3]天使玩偶【CDQ+树状数组】

2716: [Violet 3]天使玩偶

我们将问题分成四部分讨论,对于左下角、右下角、左上角、右上角四个方向分别CDQ。

用树状数组维护最大值就可以了。

#include<cstdio>
#include<cstring>
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)<(y)?(y):(x))
const int MAXN=1000005,MAXM=1000000;
int n,m,Ans[MAXN],tot;bool vis[MAXN];
struct xcw{int x,y,t;bool ty;}b[MAXN],a[MAXN],t[MAXN];
#include<cctype>
char nc(){
	static char buf[100000],*L=buf,*R=buf;
	return (L==R&&(R=(L=buf)+fread(buf,1,100000,stdin),L==R)?EOF:*L++);
}
int read(){
	int ret=0;char ch=nc();bool f=1;
	for(;!isdigit(ch);ch=nc()) f^=!(ch^'-');
	for(; isdigit(ch);ch=nc()) ret=(ret<<1)+(ret<<3)+ch-48;
	return f?ret:-ret;
}
int F[MAXM+5];
void Add(int x,int p){for(;x<=MAXM;x+=x&-x) F[x]=max(F[x],p);}
int Get(int x){int Sum=-2e6;for(;x;x-=x&-x) Sum=max(Sum,F[x]);return Sum;}
void clear(int x){for(;x<=MAXM&&F[x]!=-2e6;x+=x&-x) F[x]=-2e6;}
void Solve(int L,int R){
	if(L>=R) return;int mid=(R+L)>>1;
	Solve(L,mid),Solve(mid+1,R);
	int i=L,j=mid+1,p=L-1;
	while(i<=mid||j<=R){
		if(j>R||(i<=mid&&a[i].x<=a[j].x)){t[++p]=a[i];if(!a[i].ty) Add(a[i].y,a[i].x+a[i].y);i++;}
		else{t[++p]=a[j];if(a[j].ty) Ans[a[j].t]=min(Ans[a[j].t],a[j].x+a[j].y-Get(a[j].y));j++;}
	}
	for(int i=L;i<=mid;i++) clear(a[i].y);
	for(int i=L;i<=R;i++) a[i]=t[i];
}
void Print(int x){if(x>9) Print(x/10);putchar(x%10+48);}
int main(){
	for(int i=1;i<=MAXM;i++) F[i]=-2e6;
	memset(Ans,63,sizeof(Ans));
	n=read(),m=read();
	for(int i=1;i<=n;i++) b[i]=a[i]=(xcw){read(),read(),i,0};
	for(int i=1;i<=m;i++){
		int Type=read(),x=read(),y=read();
		b[i+n]=a[i+n]=(xcw){x,y,i+n,vis[i+n]=Type-1};
	}
	Solve(1,n+m);
	for(int i=1;i<=n+m;i++) a[i]=b[i],a[i].x=MAXM-a[i].x;
	Solve(1,n+m);
	for(int i=1;i<=n+m;i++) a[i]=b[i],a[i].y=MAXM-a[i].y;
	Solve(1,n+m);
	for(int i=1;i<=n+m;i++) a[i]=b[i],a[i].x=MAXM-a[i].x,a[i].y=MAXM-a[i].y;
	Solve(1,n+m);
	for(int i=n+1;i<=n+m;i++) if(vis[i]) Print(Ans[i]),putchar('\n');
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_41357771/article/details/88647742