【BZOJ 3476】 线段树===

59  懒惰的奶牛
贝西所在的牧场,散落着 N 堆牧草,其中第 i 堆牧草在 ( Xi,Yi ) 的位置,数量有 Ai 个单位。
贝西从家移动到某一堆牧草的时候,只能沿坐标轴朝正北、正东、正西、正南这四个方向移
动,所以计算贝西和牧草间的距离时,应采用“曼哈顿距离”—— (x,y ) 和 (x ,y ) 之间的距离为
|x ? x | + |y ? y |。例如贝西的家在 (0.5, 0.3),有一堆牧草在 (3, 2),那么它们之间的距离就是 4.2。
贝西懒得走动,她想请你为它寻找一个最好的位置作为家,这个家附近距离不超过 K 的牧草数
量之和是最大的。注意家的坐标可以不是整数,也可以和某堆牧草的坐标完全重合。
输入格式
? 第一行:两个整数 N K, 1 N 100000, 1 K 2000000
? 第二行到第 N + 1 行:第 i + 1 行有三个整数: Ai, Xi Yi, 1 Ai 10000, 0 Xi,Yi
1000000
输出格式
? 单个整数:表示距离和最佳位置不超过 K 的牧草数量之和
样例输入
4 3
7 8 6
3 0 0
4 6 0
1 4 2
样例输出
8
解释
选择 (3, 0) 为家,位置在 (0, 0), (6, 0) 和
(4, 2) 的牧草距离家都不超过 K
来源
The Lazy Cow, 2014 Mar

【分析】

  曼哈顿距离的话,那个范围,应该是一个边长和坐标轴呈45度角的正方形。

  这样就有点难搞,难统计。

  我们需要把图形“旋转一下”

  旋转的目标是让正方形的边长平行于坐标轴。、

  那么,观察一下可以得到,可以把nx=x-y ny=x+y 这样就旋转过来了(其实改变了正方形的大小的,但没有关系,只要能判断出曼哈顿距离是不是<=k就好)

 然后就很简单了,用线段树维护y纵坐标,然后x横坐标线性扫描。

代码如下:

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<queue>
  7 #include<cmath>
  8 using namespace std;
  9 #define Maxn 2000010
 10
 11 struct hp
 12 {
 13     int a,ax,ay;
 14 }tt[Maxn];
 15
 16 struct node
 17 {
 18     int l,r,lc,rc,ans;
 19     int lazy;
 20 }t[2*Maxn];int len;
 21
 22 int mymin(int x,int y) {return x<y?x:y;}
 23 int mymax(int x,int y) {return x>y?x:y;}
 24
 25 bool cmp(hp x,hp y) {return x.ax<y.ax;}
 26 int n,k;
 27
 28 int build(int l,int r)
 29 {
 30     int x=++len;
 31     t[x].l=l;t[x].r=r;
 32     t[x].ans=0;t[x].lazy=0;
 33     if(l!=r)
 34     {
 35         int mid=(l+r)>>1;
 36         t[x].lc=build(l,mid);
 37         t[x].rc=build(mid+1,r);
 38     }
 39     else t[x].lc=t[x].rc=0;
 40     return x;
 41 }
 42
 43 void init()
 44 {
 45     scanf("%d%d",&n,&k);
 46     int mx=0;
 47     for(int i=1;i<=n;i++)
 48     {
 49         scanf("%d%d%d",&tt[i].a,&tt[i].ax,&tt[i].ay);
 50         int xx=tt[i].ax;
 51         tt[i].ax=tt[i].ax-tt[i].ay;tt[i].ay=xx+tt[i].ay+1;
 52         mx=mymax(mx,tt[i].ay);
 53     }
 54     sort(tt+1,tt+1+n,cmp);
 55     k=k*2;
 56     build(1,mx);
 57 }
 58
 59 void upd(int x)
 60 {
 61     if(t[x].lazy==0) return;
 62     t[x].ans+=t[x].lazy;
 63     int lc=t[x].lc,rc=t[x].rc;
 64     if(t[x].l!=t[x].r)
 65     {
 66         t[lc].lazy+=t[x].lazy;
 67         t[rc].lazy+=t[x].lazy;
 68     }
 69     t[x].lazy=0;
 70 }
 71
 72 void change(int x,int l,int r,int y)
 73 {
 74     if(t[x].l==l&&t[x].r==r)
 75     {
 76         t[x].lazy+=y;
 77         return;
 78     }
 79     upd(x);
 80     int mid=(t[x].l+t[x].r)>>1;
 81     if(r<=mid) change(t[x].lc,l,r,y);
 82     else if(l>mid) change(t[x].rc,l,r,y);
 83     else
 84     {
 85         change(t[x].lc,l,mid,y);
 86         change(t[x].rc,mid+1,r,y);
 87     }
 88     upd(t[x].lc);upd(t[x].rc);
 89     t[x].ans=mymax(t[t[x].lc].ans,t[t[x].rc].ans);
 90 }
 91
 92 void ffind()
 93 {
 94     int j=0,ans=0;
 95     for(int i=1;i<=n;i++)
 96     {
 97         while(j<n&&tt[j+1].ax-tt[i].ax<=k)
 98         {
 99             int nx=mymax(1,tt[j+1].ay-k);
100             change(1,nx,tt[j+1].ay,tt[j+1].a);
101             j++;
102         }
103         upd(1);
104         ans=mymax(ans,t[1].ans);
105         change(1,mymax(1,tt[i].ay-k),tt[i].ay,-tt[i].a);
106     }
107     printf("%d\n",ans);
108 }
109
110 int main()
111 {
112     init();
113     ffind();
114     return 0;
115 }

bzoj 3476

2016-10-31 11:25:57

时间: 2024-10-15 06:21:43

【BZOJ 3476】 线段树===的相关文章

bzoj 1018 线段树维护连通性

本题将一道LCT的题特殊化(支持加边和删边,询问图的连通性),将图变成了2×m的网格图,然后就神奇地可以用线段树来维护. 对于每个区间[l,r],维护其四个角落之间的连通性(仅仅通过[l,r]这段的边构建起的连通性). 查询[l,r]时,先计算出[1,l-1],[l,r],[r+1,c]这三个线段的连通性,然后将[l,r]的四个角变成并查集的4个点,先用[l,r]中的6种关系更新,在看是否可以从左上角的点通过左边区间绕道左下角,以及从右上角通过右边区间绕道右下角,该并的并起来后直接看查询的点是否

BZOJ 3681 线段树合并+网络流

思路: 暴力建图有n*m条边 考虑怎么优化 (那就只能加个线段树了呗) 然后我就不会写了..... 抄了一波题解 //By SiriusRen #include <bits/stdc++.h> using namespace std; const int N=10050,M=N*100,inf=0x3f3f3f3f; vector<int>vec[N]; int n,m,first[M],next[M],v[M],w[M],tot,cnt=2,S=1,T=2; int lson[M

BZOJ 1012 线段树或单调队列

1012: [JSOI2008]最大数maxnumber 题意:两种操作:1.查询当前数列中末尾L个数中的最大的数:2.当前数列末尾插入一个数. tags:水题 线段树 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define FF(i,a,b) for (int i=a;i<=b;i++) #define F(i

BZOJ 4756 线段树合并(线段树)

思路: 1.最裸的线段树合并 2. 我们可以观察到子树求一个东西 那我们直接DFS序好了 入队的时候统计一下有多少比他大的 出的时候统计一下 减一下 搞定~ 线段树合并代码: //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=100050; int n,col[N],cpy[N],tree[N*100],lso

BZOJ 1018 线段树维护图的连通性问题

思路: 我们可以搞一棵线段树 对于一段区间有6种情况需要讨论 左上右下.左上右上.左下右下.左下右上 这四种比较好维护 用左上右下举个例子吧 就是左儿子的左上右下&左区间到右区间下面有路&右儿子的左下右下 或者是左儿子的左上右上&左区间到右区间上面有路&右儿子的左上右下 还有两种  区间的左(右)端点上下能不能联通 需要维护 这种就是左儿子的上下连通或(左上右上&左上右下&左到右两条路都联通&右儿子的上下联通) (假设c1<c2) 最后要查的是

bzoj 3211 线段树

开方操作最多进行5次就可以把出现的任何数变成1. 所以用线段树暴力修改,以后修改时只需看一下是否当前区间都是0或1,如果是那么就直接返回. 1 /************************************************************** 2 Problem: 3211 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:1976 ms 7 Memory:7068 kb 8 **************

BZOJ 1012 线段树||单调队列

非常裸的线段树  || 单调队列: 假设一个节点在队列中既没有时间优势(早点入队)也没有值优势(值更大),那么显然不管在如何的情况下都不会被选为最大值. 既然它仅仅在末尾选.那么自然能够满足以上的条件. 线段树 #include "stdio.h" #include "string.h" struct node { int l,r,Max; }data[800010]; int Max(int a,int b) { if (a<b) return b; els

bzoj 3999 线段树区间提取 有序链剖

看错题目了,想成每个城市都可以买一个东西,然后在后面的某个城市卖掉,问最大收益.这个可以类似维护上升序列的方法在O(nlog^3n)的时间复杂度内搞定 这道题用到的一些方法: 1. 可以将有关的线段提取出来,然后一起处理. 2. 线段树可以维护两个方向的信息,这样就可以处理树上有序的东西. 1 /************************************************************** 2 Problem: 3999 3 User: idy002 4 Langu

bzoj 1858 线段树

思路:很明显的线段树,随便搞搞lazy标记,维护一下区间最长的1. #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PII pair<int, int> #define y1 skldjfskldjg #define y2 skldfjsklejg using namespace std; const int N

BZOJ 4262 线段树+期望

思路: 把询问离线下来,查询max和查询min相似,现在只考虑查询max 令sum[l,r,x]表示l到r内的数为左端点,x为右端点的区间询问的答案 那么询问就是sun[l1,r1,r2]-sum[l1,r1,l1-1] 从1到n枚举x,维护区间线段树表示sum[l,r,x],发现从x-1转移到x的过程中,每个数加上了max(a[pos]..a[x])的答案. 用单调队列维护一个单调递减的序列,由于a数列是随机的,这个队列期望有log个元素,所以只需要对这log段暴力修改,复杂度nlog^2n