「学习笔记」二维数点

二维数点问题的一个小整理

(1.)LGOJ2163 [SHOI2007]园丁的烦恼

这个题就是最基本的二维数点问题了,把题读懂了就很明白

这个题和后面的摩基亚唯一的区别就是这个题的插入操作全部在查询前面

我们离线所有操作,然后先离散化 坐标 的,(x) 一维,(y) 一维

我们把 (x) 当做下标(这里需要意会一下),其实在后面就可以被描述为主席树的版本

但是这个题我们 (sort) 完了,有一维的偏序就搞定了

(y) 当做树状数组对应位置的下标,然后直接插入 (+) 容斥就没了

如果容斥没听说过,右转:激光炸弹

(Code)

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	const int N=5e5+10;
	int n,r;
	struct BIT{
		int c[10000003];
		inline int lowbit(int x){return x&(-x);}
		inline void add(int x,int d){for(;x<=r;x+=lowbit(x)) c[x]+=d; return ;}
		inline int ask(int x){int res=0; for(;x;x-=lowbit(x)) res+=c[x]; return res;}
	}t;
	struct node{
		int x,y,id,opt;
	}q[N<<2];
	int cnt,now=1,T;
	struct trr{
		int x,y;
	}a[N];
	inline bool cmp(trr d,trr e)
	{
		if(d.x!=e.x) return d.x<e.x;
		return d.y<e.y;
	}
	inline bool cmp2(node d,node e)
	{
		if(d.x!=e.x) return d.x<e.x;
		return d.y<e.y;
	}
	int ans[N];
	signed main()
	{
		n=read(); T=read();
		for(int i=1;i<=n;++i) 
		{
			a[i].x=read()+1; a[i].y=read()+1;
			r=max(r,a[i].y);
		} 
		for(int i=1;i<=T;++i)
		{
			int x1=read()+1,y1=read()+1,x2=read()+1,y2=read()+1;
			q[++cnt]=(node){x1-1,y1-1,i,1}; q[++cnt]=(node){x1-1,y2,i,-1};
			q[++cnt]=(node){x2,y1-1,i,-1}; q[++cnt]=(node){x2,y2,i,1}; 
			r=max(r,y2);
		}
		
		sort(a+1,a+n+1,cmp); sort(q+1,q+cnt+1,cmp2); 
		
		for(int i=1;i<=cnt;++i)
		{
			while(now<=n&&a[now].x<=q[i].x) t.add(a[now].y,1),++now;
			ans[q[i].id]+=q[i].opt*t.ask(q[i].y);
		}
		for(int i=1;i<=T;++i) cout<<ans[i]<<endl;
		return 0;
	}
}
signed main(){return yspm::main();}

(2.) HDU5140

这题就是上一个题的强制在线版本

由上一题的描述中的蛛丝马迹,我们发现可以用主席树解决本问题

按照原来的套路,两维离散化,然后是正常的主席树插入查询操作

(和上一个题的思想是类似的,但是这个题直接算,不用容斥)

这题注意点:我们在相同的 (x) 坐标可能有好多的主席树,但是我们必须创新树

否则会信息覆盖

或者直接(p=rt[i] pre=rt[i])

我的代码不知为啥会在 (hdu) 挂……

可能是清空的时候细节有问题吧……

但是查询啥的正确性显然

(Code)

#include <bits/stdc++.h>
using namespace std;
#define sr int
#define int long long
namespace yspm {
inline int read() {
    int res = 0, f = 1;
    char k;
    while (!isdigit(k = getchar()))
        if (k == '-')
            f = -1;
    while (isdigit(k)) res = res * 10 + k - '0', k = getchar();
    return res * f;
}
const int N = 1e5 + 10;
struct node {
    int ls, rs, sum;
#define ls(p) t[p].ls
#define rs(p) t[p].rs
#define sum(p) t[p].sum
} t[N*25];
int tot, rt[N];
inline void push_up(int p) { return sum(p) = sum(ls(p)) + sum(rs(p)), void(); }
inline void update(sr &p, sr pre, sr l, sr r, sr x, int val) {
    p = ++tot;
    t[p] = t[pre];
    if (l == r)
        return sum(p) += val, void();
    int mid = (l + r) >> 1;
    if (x <= mid)
        update(ls(p), ls(pre), l, mid, x, val);
    else
        update(rs(p), rs(pre), mid + 1, r, x, val);
    return push_up(p);
}
inline int query(sr l, sr r, sr st, sr ed, sr fr, sr to) {
    if (fr <= st && ed <= to)
        return sum(r) - sum(l);
    sr mid = (st + ed) >> 1, ans = 0;
    if (fr <= mid)
        ans += query(ls(l), ls(r), st, mid, fr, to);
        
    if (to > mid)
        ans += query(rs(l), rs(r), mid + 1, ed, fr, to);
    return ans;
}
int l[N], a[N], s[N], ans, b[N], m, c[N], p, n;
struct tmp {
    int s, l, a;
    bool operator<(const tmp &y) const { return a < y.a; }
} w[N];
inline void work() {
    m = 0;
    p = 0;
    tot = 0;
    ans = 0;
    memset(b, 0, sizeof(b));
    memset(c, 0, sizeof(c));
    memset(rt, 0, sizeof(rt));
    memset(t, 0, sizeof(t));
    for (int i = 1; i <= n; ++i) s[i] = read(), l[i] = read(), a[i] = read(), b[++m] = a[i], c[++p] = l[i];
    sort(b + 1, b + m + 1); 
    sort(c + 1, c + p + 1);
    p = unique(c + 1, c + p + 1) - c - 1;
    for (int i = 1; i <= n; ++i)
        a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b, l[i] = lower_bound(c + 1, c + p + 1, l[i]) - c;
    for (int i = 1; i <= n; ++i) w[i] = (tmp){ s[i], l[i], a[i] };
    sort(w + 1, w + n + 1);
    for (int i = 1; i <= n; ++i) update(rt[i], rt[i-1], 1, p, w[i].l, w[i].s);
    int T = read();
    while (T--) {
        int x1 = read() + ans, x2 = read() - ans, y1 = read() + ans, y2 = read() - ans;
        if (x1 > x2)
            swap(x1, x2);
        if (y1 > y2)
            swap(y1, y2);
        x1 = lower_bound(c + 1, c + p + 1, x1) - c;
        x2 = upper_bound(c + 1, c + p + 1, x2) - c-1;
        y1 = lower_bound(b + 1, b + m + 1, y1) - b;
        y2 = upper_bound(b + 1, b + m + 1, y2) - b-1;
        ans = query(rt[y1 - 1], rt[y2], 1, p, x1, x2);
        printf("%lld
", ans);
    }
    return;
}
signed main() {
    while (~scanf("%lld", &n)) work();

    return 0;
}
}  // namespace yspm
signed main() { return yspm::main(); }

(3.) [BOI2007]Mokia 摩基亚

这题就是个最终版的了,带修改的

上三维偏序的各种方法都可以,我只会 (cdq)

时间一维,坐标两维

首先我们在输入的时候直接干掉了时间的一维

归并 (x) ,最后统计 (y)

挺不错的,就是手残不好调

(Code)

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar(); 
		return res*f;
	}
	const int N=2e6+10,M=2e5+10;
	struct query{
		int x,y,opt,val,ans,id;
		query(){}
		query(int a,int b,int c,int d,int e,int f)
		{
			x=a; y=b; opt=c; val=d; ans=e; id=f;
			return ;
		}
	}q[M<<2];
	int b[N],w;
	inline int lowbit(int x){return x&(-x);}
	inline int ask(int x){int res=0; for(;x;x-=lowbit(x)) res+=b[x]; return res;}
	inline void clear(int x){for(;x<=w;x+=lowbit(x)) b[x]=0; return ;}
	inline void add(int x,int d){for(;x<=w;x+=lowbit(x)) b[x]+=d; return ;}
	query t[M<<2];
	inline void cdq(int l,int r)
	{
		if(l==r) return ;
		int mid=(l+r)>>1;
		cdq(l,mid); cdq(mid+1,r);
		int c1=l,c2=mid+1,c3=l;
		while(c1<=mid&&c2<=r) 
		{
			if(q[c1].x<=q[c2].x)
			{
				if(!q[c1].opt) add(q[c1].y,q[c1].val);
				t[c3++]=q[c1++];
			}
			else 
			{
				if(q[c2].opt) q[c2].ans+=ask(q[c2].y);
				t[c3++]=q[c2++];
			}
		}
		while(c1<=mid)
		{
			if(!q[c1].opt) add(q[c1].y,q[c1].val);
			t[c3++]=q[c1++];
		}
		while(c2<=r)
		{
			if(q[c2].opt) q[c2].ans+=ask(q[c2].y);
			t[c3++]=q[c2++]; 
		}
		for(int i=l;i<=mid;++i) if(!q[i].opt) clear(q[i].y);
		for(int i=l;i<=r;++i) q[i]=t[i];
		return ;
	}
	inline bool cmp(query a,query b)
	{
		return a.id<b.id;
	}
	int cnt;
	signed main()
	{
		int tmp=read(); w=read()+1; tmp=read();
		while(tmp!=3)
		{
			if(tmp-1)
			{
				int x1=read(),y1=read(),x2=read(),y2=read();
				q[++cnt]=(query){x2+1,y2+1,1,0,0,cnt};
				q[++cnt]=(query){x1,y2+1,1,0,0,cnt};
				q[++cnt]=(query){x2+1,y1,1,0,0,cnt};
				q[++cnt]=(query){x1,y1,1,0,0,cnt};
			}
			else
			{
				int x=read(),y=read(),d=read();
				q[++cnt]=(query){x+1,y+1,0,d,0,cnt};
			}tmp=read();
		}
		cdq(1,cnt);
		sort(q+1,q+cnt+1,cmp);
		for(int i=1;i<=cnt;++i)
		{
			if(!q[i].opt) continue;
			printf("%lld
",q[i].ans-q[i+1].ans-q[i+2].ans+q[i+3].ans); i+=3;
		}
		return 0;
	}
}
signed main(){return yspm::main();}

最后:二维数点只是在一些题目中的部分或者解决一些问题的方法

一般的题目都会是以其它算法为主算法的(比如某 (IOI) 题,别问我,我就是听说而已)

原文地址:https://www.cnblogs.com/yspm/p/12713573.html