Loading

hdu6089 Rikka with Terrorist

\(n\times m\) 网格图,给一个指定的点集 \(S\)\(q\) 次询问(\(n,m,q,|S|\le 10^5\)),给定一个点 \((x,y)\),问有多少个目标点 \((x',y')\) 满足

\[\not\exist (x_0,y_0)\in S:x_0\in[\min(x',x),\max(x',x)],y_0\in[\min(y',y),\max(y',y)] \]


图都是手绘的,造成视觉障碍概不负责。

一个经典问题,目标点大概是这种形式(黑框内):

我们只考虑左上部分,剩下可以通过顺时针转四次 \(90^\circ\)(见代码)用同样的方式处理掉。注意左上指的是 \(x'<x,y'\le y\),这样转四次之后统计答案才可以不重不漏。最后会只剩 \((x,y)\) 自身没统计到,答案 \(+1\) 即可。如下图,四次旋转统计部分涵盖了除 \((x,y)\) 自身外的所有点,当前统计黑绿两框的交集。

先扫描线,\(y\) 从左到右扫。考虑统计答案的过程,是 \(x\) 从询问点坐标往上跑一遍,维护一个值 \(k\),碰到左边伸过来的一根柱子,就让 \(k\) 和柱子长度取 \(\max\),并让答案加上 \(y-k\)。"Formally",设 \(S\)\(x\) 坐标为 \(i\) 的点中,\(y\) 坐标的最大值为 \(R_i\),答案为

\[xy-\left(\sum_{i=1}^x \max_{j=i}^x R_j\right)-(x-R_x) \]

最后减去 \(x-R_x\) 是因为 \(x\) 这一行不能算,去重需要。现在问题变成了,动态修改 \(R_i\) 并询问:\(R\) 数组以 \(x\) 为右端点的前缀,对其每一个后缀 \(\max\) 求和。

这个线段树有点神奇。区间 \([l,r]\) 维护的是:\(\large \sum_{i=l}^r\max_{j=i}^r R_j\)。那么查询就是把 \([1,x]\) 拆成 \(\log\) 个区间然后依次合并。考虑如何合并 \([l_0,l-1]\)\([l,r]\)

发现,右边区间 \([l,r]\) 上每一位的贡献不变,令 \(m\) 为右区间的最大值 \(\max_{i=l}^r R_i\)\(p\) 为左边区间 \([l_0,l-1]\) 中最靠右的后缀 \(\max\) \(suf_p\ge m\) 的位置,那么 \([l_0,p]\) 上每一位的贡献也不变。唯一改变的是,\((p,l-1]\) 上的每一位都被推平成了 \(m\)

区间维护的是 \(suf\) 的和,那区间推平就不难做。考虑如何找到 \(p\)。区间额外维护 \(\max\)(也就是左端点的 \(suf\) 取值),线段树上二分找即可。那么 pushup 时要做一个递归,时间复杂度 \(\Theta(n\log^2 n)\),具体实现见代码。

//为什么这个能过????????????????
//这个咋可能能过???
//我们应该考虑扫行然后算后面的贡献
// 这个就是能过!!!
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long

using namespace std;

const int N=214514;

namespace sgt{
	#define ls(x) (x<<1)
	#define rs(x) (x<<1|1)
	struct node{
		ll s;int mx;
		#define s(x) t[x].s
		#define mx(x) t[x].mx
	}t[N<<2];
	ll fnd(int now,int ln,int rn,int x){
		if(ln==rn) return max(mx(now),x);
		int mid=ln+rn>>1;
		if(mx(rs(now))>=x) return s(now)-s(rs(now))+fnd(rs(now),mid+1,rn,x);
		else return fnd(ls(now),ln,mid,x)+1ll*x*(rn-mid);
	}
	node updata(int x,int lx,int rx,node b){
		return (node){b.s+fnd(x,lx,rx,b.mx),max(mx(x),b.mx)};
	}
	void upd(int now,int ln,int rn,int p,int y){
		if(ln==rn) return s(now)=mx(now)=y,void();
		int mid=ln+rn>>1;
		if(p<=mid) upd(ls(now),ln,mid,p,y);
		else upd(rs(now),mid+1,rn,p,y);
		t[now]=updata(ls(now),ln,mid,t[rs(now)]);
	}
	node qry(int now,int ln,int rn,int r){
		if(r<ln) return (node){0,0};
		if(rn<=r) return t[now];
		int mid=ln+rn>>1;
		if(r<=mid) return qry(ls(now),ln,mid,r);
		else return updata(ls(now),ln,mid,qry(rs(now),mid+1,rn,r));
	}
}

int x[N],y[N],n,m,K,Q;

void rotat(){
	for(int i=1;i<=K+Q;++i){
		int a=m-y[i]+1,b=x[i];
		x[i]=a;y[i]=b;
	}
	swap(n,m);
}

int p[N],q[N],cur[N];ll ans[N];

void fuc(){
	memset(sgt::t,0,sizeof(sgt::t));memset(cur,0,sizeof(cur));
	sort(p+1,p+K+1,[](int a,int b){return y[a]<y[b];});
	sort(q+1,q+Q+1,[](int a,int b){return y[a]<y[b];});
	for(int i=1,a=0,b=0;i<=m;++i){
		while(a<K&&y[p[a+1]]==i){++a;
			sgt::upd(1,1,n,x[p[a]],i);cur[x[p[a]]]=i;
		}
		while(b<Q&&y[q[b+1]]==i){++b;
			ans[q[b]-K]+=1ll*i*x[q[b]]-sgt::qry(1,1,n,x[q[b]]).s-i+cur[x[q[b]]];
		}
	}
}

int main()
{
	scanf("%d%d%d%d",&n,&m,&K,&Q);
	for(int i=1;i<=K;++i) p[i]=i,scanf("%d%d",x+i,y+i);
	for(int i=K+1;i<=K+Q;++i) q[i-K]=i,scanf("%d%d",x+i,y+i);
	int t=4;while(t--) fuc(),rotat();
	for(int i=1;i<=Q;++i) printf("%lld\n",ans[i]+1);
}
posted @ 2023-07-20 19:19  Albertvαn  阅读(13)  评论(0编辑  收藏  举报