【线段树】【扫描线】Petrozavodsk Winter Training Camp 2018 Day 5: Grand Prix of Korea, Sunday, February 4, 2018 Problem A. Donut

题意:平面上n个点,每个点带有一个或正或负的权值,让你在平面上放一个内边长为2l,外边长为2r的正方形框,问你最大能圈出来的权值和是多少?

容易推出,能框到每个点的 框中心 的范围也是一个以该点为中心的相同大小的框。

于是,把每个点的框拆成4条线。从下往上扫过去,最下面的线,给[R,R]区间加上该点的权值,然后上面再给[L,L]减去,然后上面再给[L,L]加上,然后再往上在给[R,R]减去即可。每次扫完一行,就用线段树的全局最大值尝试更新答案。

两个坑点:首先,由于线段树里存储的是离散后的点,所以有可能会存在一些死角,所以给离散化后的每两个点之间再插入一个点,这样不会有死角。

其次,出边对应的线要向上移动一个单位,并且左右端点向内缩小一个单位,这样方便处理。

(UPDATE:虽然AC了此题,但是被HACK了,怀疑是“出边对应的线要向上移动一个单位,并且左右端点向内缩小一个单位”这里有BUG)

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
struct data{
	int p;
	ll v;
}t[1200005];
int a[1200005];
bool cmp(const data &a,const data &b){
	return a.v<b.v;
}
struct LINE{
	int y;
	int l,r;
	int w;
	bool in;
}lines[400005];
bool cm2(const LINE &a,const LINE &b){
	return a.y!=b.y ? a.y<b.y : a.in>b.in;
}
int n,L,R,e,zy;
int y[100005],z[100005],ans;
int maxv[4800005],delta[4800005];
void pushdown(int rt){
	if(delta[rt]){
		delta[rt<<1]+=delta[rt];
		delta[rt<<1|1]+=delta[rt];
		maxv[rt<<1]+=delta[rt];
		maxv[rt<<1|1]+=delta[rt];
		delta[rt]=0;
	}
}
void update(int ql,int qr,int v,int rt,int l,int r){
	if(ql<=l && r<=qr){
		maxv[rt]+=v;
		delta[rt]+=v;
		return;
	}
	int m=(l+r>>1);
	pushdown(rt);
	if(ql<=m){
		update(ql,qr,v,rt<<1,l,m);
	}
	if(m<qr){
		update(ql,qr,v,rt<<1|1,m+1,r);
	}
	maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
}
int main(){
//	freopen("a.in","r",stdin);
	int x;
	scanf("%d%d%d",&n,&L,&R);
	for(int i=1;i<=n;++i){
		scanf("%d%d%d",&x,&y[i],&z[i]);
		t[++e].v=(ll)(x-R)*2ll;
		t[e].p=e;
		t[++e].v=(ll)(x-R)*2ll-1ll;
		t[e].p=e;
		t[++e].v=(ll)(x-R)*2ll+1ll;
		t[e].p=e;

		t[++e].v=(ll)(x-L+1)*2ll;
		t[e].p=e;
		t[++e].v=(ll)(x-L+1)*2ll-1ll;
		t[e].p=e;
		t[++e].v=(ll)(x-L+1)*2ll+1ll;
		t[e].p=e;

		t[++e].v=(ll)(x+L-1)*2ll;
		t[e].p=e;
		t[++e].v=(ll)(x+L-1)*2ll-1ll;
		t[e].p=e;
		t[++e].v=(ll)(x+L-1)*2ll+1ll;
		t[e].p=e;

		t[++e].v=(ll)(x+R)*2;
		t[e].p=e;
		t[++e].v=(ll)(x+R)*2-1;
		t[e].p=e;
		t[++e].v=(ll)(x+R)*2+1;
		t[e].p=e;
	}
	sort(t+1,t+e+1,cmp);
	a[t[1].p]=++zy;
	for(int i=2;i<=e;++i){
		if(t[i].v!=t[i-1].v){
			++zy;
		}
		a[t[i].p]=zy;
	}
	for(int i=1;i<=n;++i){
		lines[i*4-3].y=y[i]-R;
		lines[i*4-2].y=y[i]-L+1;
		lines[i*4-1].y=y[i]+L;
		lines[i*4-0].y=y[i]+R+1;

		lines[i*4-3].l=a[i*12-11];
		lines[i*4-2].l=a[i*12-8];
		lines[i*4-1].l=a[i*12-8];
		lines[i*4-0].l=a[i*12-11];

		lines[i*4-3].r=a[i*12-2];
		lines[i*4-2].r=a[i*12-5];
		lines[i*4-1].r=a[i*12-5];
		lines[i*4-0].r=a[i*12-2];

		lines[i*4-3].w=z[i];
		lines[i*4-2].w=z[i];
		lines[i*4-1].w=z[i];
		lines[i*4-0].w=z[i];

		lines[i*4-3].in=1;
		lines[i*4-2].in=0;
		lines[i*4-1].in=1;
		lines[i*4-0].in=0;
	}
	sort(lines+1,lines+(n<<2|1),cm2);
	for(int i=1;i<=(n<<2);++i){
		if(lines[i].in){
			update(lines[i].l,lines[i].r,lines[i].w,1,1,zy);
		}
		else{
			update(lines[i].l,lines[i].r,-lines[i].w,1,1,zy);
		}
		if(lines[i].y!=lines[i+1].y){
			ans=max(ans,maxv[1]);
		}
	}
	printf("%d\n",ans);
	return 0;
}

原文地址:https://www.cnblogs.com/autsky-jadek/p/8975493.html

时间: 2024-09-28 20:57:49

【线段树】【扫描线】Petrozavodsk Winter Training Camp 2018 Day 5: Grand Prix of Korea, Sunday, February 4, 2018 Problem A. Donut的相关文章

【博弈论】【SG函数】【线段树】Petrozavodsk Summer Training Camp 2016 Day 9: AtCoder Japanese Problems Selection, Thursday, September 1, 2016 Problem H. Cups and Beans

一开始有n个杯子,每个杯子里有一些豆子,两个人轮流操作,每次只能将一个豆子移动到其所在杯子之前的某个杯子里,不过可以移动到的范围只有一段区间.问你是否先手必胜. 一个杯子里的豆子全都等价的,因为sg函数是异或起来的值,所以一个杯子里如果有偶数个豆子,就没有意义. 用sg(i)表示i杯子中的豆子的sg值,sg(i)就是其所能移动到的那段杯子的区间的sg值的mex(未出现的最小非负整数).可以用线段树去做.是经典问题. 由于每次看似是后缀询问,实则是全局询问,故而主席树完全是多余的. 回顾一下区间m

2015 UESTC Winter Training #7【2010-2011 Petrozavodsk Winter Training Camp, Saratov State U Contest】

2015 UESTC Winter Training #7 2010-2011 Petrozavodsk Winter Training Camp, Saratov State U Contest 据说这套题写出3题就是金牌水平了... Problem D. Group Stage 足球比赛,现在有n(2<= n <=100)支球队.要从中选拔m(1<= m <=n-1)支球队,每支球队互相比赛一场,即比赛n*(n-1)/2场,胜者得3分,平局各得1分.最后从大到小列出排名,如果有

【二分】Petrozavodsk Winter Training Camp 2017 Day 1: Jagiellonian U Contest, Monday, January 30, 2017 Problem A. The Catcher in the Rye

一个区域,垂直分成三块,每块有一个速度限制,问你从左下角跑到右上角的最短时间. 将区域看作三块折射率不同的介质,可以证明,按照光路跑时间最短. 于是可以二分第一个入射角,此时可以推出射到最右侧边界上的位置,看什么时候恰好射到右上角即可. 这份sb代码貌似挂精度了. #include<cstdio> #include<iostream> #include<cmath> using namespace std; #define EPS 0.0000000001 int T,

2015-2016 Petrozavodsk Winter Training Camp, Makoto rng_58 Soejima Сontest 4题解

传送门 被\(jz\)姐姐带着飞--代码的话直接在上面找吧 \(A\) 发现这样的数不会很多,直接把所有的都打表打出来就行了 \(B\) 转换成切比雪夫距离后,每个点分别向\(x,y\)坐标最大最小的点连边,跑个最大生成树就行了 \(C\) 最终的柿子一定行如\(s\pm t=2a_{i_1}-2a_{i_2}+...\),那么直接\(bfs\)出所有可能的结果,然后根据奇偶性判断一下就行了 原文地址:https://www.cnblogs.com/yuanquming/p/11715366.h

【BZOJ】1382: [Baltic2001]Mars Maps (线段树+扫描线)

1382: [Baltic2001]Mars Maps Time Limit: 5 Sec  Memory Limit: 64 MB Description 给出N个矩形,N<=10000.其坐标不超过10^9.求其面积并 Input 先给出一个数字N,代表有N个矩形. 接下来N行,每行四个数,代表矩形的坐标. Output 输出面积并 Sample Input 2 10 10 20 20 15 15 25 30 Sample Output 225 本以为是傻逼题,没想到不容易啊- 线段树+扫描

BZOJ 4059 Cerc2012 Non-boring sequences 线段树+扫描线

题目大意:定义一个序列为[不无聊的]当且仅当这个序列的任意一个区间都存在一个数只出现过一次,给定一个序列,要求判断这个序列是否是[不无聊的] 定义lasti表示第i个元素上一次出现的位置(第一次出现则为0),nexti表示第i个元素下一次出现的位置(最后一次出现则为n+1),那么这个元素能成为某个区间仅出现一次的数,当且仅当这个区间的左端点在[lasti+1,i]之间,右端点在[i,nexti?1]之间 我们可以将区间的左右端点放在二维平面上,那么一个元素产生的贡献是一个矩形,我们要确定的是所有

HDU 4419 Colourful Rectangle --离散化+线段树扫描线

题意: 有三种颜色的矩形n个,不同颜色的矩形重叠会生成不同的颜色,总共有R,G,B,RG,RB,GB,RGB 7种颜色,问7种颜色每种颜色的面积. 解法: 很容易想到线段树扫描线求矩形面积并,但是如何维护每种颜色的长度着实让我伤透了脑筋.后来看了一位朋友的题解,才幡然醒悟. 开始想到了用二进制表示颜色,R用001表示,G用010表示,B用100表示.那么就可以用十进制1~7表示7种不同颜色了. 维护 cov[rt][1~3] 表示此区间内3种原色各有多少个, Len[rt][i]表示每种颜色的长

sgu316Kalevich Strikes Back(线段树+扫描线)

做法:总体想法是求出一个矩形的面积以及它所包含的矩形,然后用自己的面积减掉所包含的.主要问题是怎样求解它所包含的矩形. 因为是没有相交点的,可以利用扫描线的方法去做,类似染色,当前段如果是x色,也就是第x个矩形,那么再把他染成y颜色时,说明x包含y,而当扫到y的上边时,这一段又恢复到x色.标记一下被包含的矩形,记录所包含的矩形. 因为会有恢复染色操作,up需要时时更新,左儿子和右儿子一样颜色时就可以合并为一段. 1 ; 2 } 3 void down(int w) 4 { 5 if(s[w]!=

hdu1255 覆盖的面积 线段树-扫描线

矩形面积并 线段树-扫描线裸题 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include<math.h> 5 using namespace std; 6 const int maxm=2e3+5; 7 const double eps=1e-5; 8 9 struct seg{ 10 double x,y1,y2; 11 int c; 12 bool operator