扫描线

扫描线

参考博客

注解1:横着的扫描线,我们把矩形拆成上下两条线段,下面那条 k=1,上面的k= -1;

h 就是到x轴的距离;val[ ]是线段长度

记得离散化 (代码里的 x[] )

#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 1000050
using namespace std;
typedef long long ll;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,root,ax,ay,bx,by,tot,tree_cnt;
ll ans;
int x[N],ls[N],rs[N];
ll tag[N],val[N];
struct line{
	int l,r,h,k;//k=1/-1 注解1
    line(){}
    line(int a,int b,int c,int d):l(a),r(b),h(c),k(d){}
    bool operator < (const line &x)const{
        return h<x.h;
    }
}li[N];
void pushup(int p,int l,int r){
    if(tag[p]) val[p]=x[r+1]-x[l];//避免线段重叠
    else val[p]=val[ls[p]]+val[rs[p]];
}
void modify(int L,int R,int l,int r,ll add,int &p){
    if(!p) p=++tree_cnt;
	if(L<=l&&r<=R){
        tag[p]+=add;
        pushup(p,l,r);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) modify(L,R,l,mid,add,ls[p]);
    if(R>mid) modify(L,R,mid+1,r,add,rs[p]);
    pushup(p,l,r);
}
int main(){
	n=read();
	for(int i=1;i<=n;++i){
		ax=read(),ay=read(),bx=read(),by=read();
		x[i<<1]=ax,x[(i<<1)-1]=bx;
		li[i<<1]=(line){ax,bx,ay,1};  
		li[(i<<1)-1]=(line){ax,bx,by,-1};
	}
	sort(x+1,x+2*n+1);
	sort(li+1,li+2*n+1);
	tot=unique(x+1,x+2*n+1)-x-1;
	for(int i=1;i<=2*n;++i){
		int l=lower_bound(x+1,x+tot+1,li[i].l)-x;
		int r=lower_bound(x+1,x+tot+1,li[i].r)-x-1;
        ans+=val[root]*(li[i].h-li[i-1].h);
        modify(l,r,1,tot,li[i].k,root);
    }
    printf("%lld",ans);
	return 0;
}

窗口的星星

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1000010;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,w,h;
struct node{
	int x,y,a;	
	bool operator < (const node &g) const {
		return x<g.x;
	} 
}p[N];
int b[N],tot;

#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
int mx[N*4],tag[N*4];
void build(int l,int r,int p) {
	mx[p]=tag[p]=0;
	if(l==r) return;
	build(l,mid,ls);
	build(mid+1,r,rs);
}

void pushdown(int p) {
	if(!tag[p]) return;
	tag[ls]+=tag[p];mx[ls]+=tag[p];
	tag[rs]+=tag[p];mx[rs]+=tag[p];
	tag[p]=0;
}

inline void Max(int &x,int y){if(x<y)x=y;}

void modify(int l,int r,int L,int R,int v,int p) {
	if(L<=l&&r<=R) {
		mx[p]+=v;tag[p]+=v;return;
	}
	pushdown(p);
	if(L<=mid) modify(l,mid,L,R,v,ls);
	if(R>mid) modify(mid+1,r,L,R,v,rs);
	mx[p]=max(mx[ls],mx[rs]);
}

inline void add(int y,int x) {
	int R=lower_bound(b+1,b+1+tot,y+h)-b-1;//濞夈劍鍓版潏鍦櫕娑撳秷鍏橀崣鏍у煂
	int L=lower_bound(b+1,b+1+tot,y)-b;
	modify(1,tot,L,R,x,1);
}

int main() {
		n=read();w=read();h=read();
		tot=0;
		for(int i=1;i<=n;i++) {
			p[i].x=read();p[i].y=read();p[i].a=read();
			b[++tot]=p[i].y;
		}
		sort(p+1,p+1+n);
		sort(b+1,b+1+tot);
		tot=unique(b+1,b+1+tot)-b-1;
		build(1,tot,1);
		int pre=1;
		int ans=0;
		for(int i=1;i<=n;i++) {
			while(p[i].x-p[pre].x>=w) add(p[pre].y,-p[pre].a),pre++;
			add(p[i].y,p[i].a);
			Max(ans,mx[1]);
		}
		printf("%d\n",ans);
	return 0;
}

poj3109 Inner Vertices

https://blog.csdn.net/lolicon480/article/details/44183397

一个无限大的棋盘上有无数个点,这些点有黑有白,下面进行一步操作,如果有一个点,上下左右各有点,则将这个点涂成黑色。最后计算这个棋盘上黑点的数量。

Solution

树状数组

坐标离散化

果我们知道确定了同一列中的两点,这两点之间要被涂黑的点的数量就等于这一列的左右各有点的行数之和。因此我们可以借助扫描线算法,先维护一个数组sum,从左向右扫描,碰到点,如果不是这一行的末点,且前面没有碰到点的话,设这一行的坐标为x,那么sum[x]就加一,碰到末点sum[x]就减1。这样,同一列中的两点的横坐标为x1,x2,即可通过计算sum[x1+1]+sum[x1+2]+...sum[x2]可以获得这两点间应增加的黑点数。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=200005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
int n;
struct node{
	int x,y;
}a[N];
bool cmpx(node a,node b) {
	return a.x==b.x?a.y<b.y:a.x<b.x;
}
bool cmpy(node a,node b) {
	return a.y==b.y?a.x<b.x:a.y<b.y;
}
bool vis[N];
int ed[N];
int c[N];
inline void upd(int x,int k){for(;x<=n;x+=x&(-x))c[x]+=k;}
inline int sum(int x){int res=0;for(;x>0;x-=x&(-x))res+=c[x];return res;}
int main() {
	n=read();
	for(int i=1;i<=n;i++)
		a[i].x=read(),a[i].y=read();
    sort(a+1,a+n+1,cmpx);
    int xn=1;
    for(int i=1;i<=n;i++) {
        int flag=0;
        if(a[i].x!=a[i+1].x) flag=1;
        a[i].x=xn;
        if(flag==1) xn++;
    }
    sort(a+1,a+n+1,cmpy);
    int yn=1;
    for(int i=1;i<=n;i++) {
        int flag=0;
        if(a[i].y!=a[i+1].y) flag=1;
        a[i].y=yn;
        if(flag==1) yn++;
    }
	for(int i=1;i<=n;i++) 
		Max(ed[a[i].x],a[i].y);
	int ans=n;
	for(int i=1;i<n;i++) {
		if(vis[a[i].x]==0&&a[i].y!=ed[a[i].x]) {
			vis[a[i].x]=1;
			upd(a[i].x,1);
		}
		if(a[i].y==a[i+1].y&&a[i].x!=a[i+1].x) 
			ans+=sum(a[i+1].x-1)-sum(a[i].x);
		if(vis[a[i].x]==1&&a[i].y==ed[a[i].x]) {
			vis[a[i].x]=0;
			upd(a[i].x,-1);
		}
	}
	printf("%d\n",ans);
	return 0;
}


祭坛

在平面内找一点,使得它的四个正方向上的点的数量的最小值最大

O(nlogn)

一行一行处理,用线段树记录一行中上方下方水晶的较小值的区间最大值,每一行维护 suml和 sumr。
处理完一行就用这一行的水晶更新线段树。
如此可以得出第一个答案。
然后再按上述扫一遍,用线段树记录一行里上下水晶大于 ans的个数,每一行扫描一遍,再更新线段树。

\[O(nlog^2n)$$的二分+树状数组解法,如果常数写的好比 nlogn跑得快。 首先二分一个答案,然后用扫描线的思想维护两个数组up[i], down[i]表示第i列的上方有多少点,下方有多少点, 那么满足题意的点一定满足up[i] >= t, down[i] >= t(t为二分的答案),这是竖列的要求。 然后对于横行的要求的话,我们找到可行的左端点和右端点, 在这个区间内用树状数组维护一下满足数列要求的点,查询一个区间和即可 https://www.cnblogs.com/jianglangcaijin/p/4197972.html\]

posted @ 2020-08-14 00:05  ke_xin  阅读(97)  评论(0编辑  收藏  举报