Vijos 3091 长跑

来源:互联网 发布:windows程序设计最新版 编辑:程序博客网 时间:2024/04/28 15:49

【问题描述】  

       小沐近段时间非常能吃,身体日渐胖了起来。他父亲担心他会长的过于肥胖,要求他每天坚持长跑锻炼。  

       父亲为他设计了一条长跑路线,依次有n个必经点,必经点表示为平面上的坐标(x,y),相邻两个必经点的(x1,y1)和(x2,y2)距离表示为|x1-x2 | + |y1-y2 |。同时,为了让跑步不那么单调,父亲还会时常改变其中的某些必经点的位置。 每一次长跑会,父亲会为小沐指定一条长跑路线的子路线,一条子路线是整条路线中包含若干连续必经点的一段,同时允许他可以省略其中的一个必经点(即直接绕过这个点到下一个点),但是省略的必经点不能是子路线的起点和终点。  

       那么问题来了,小沐想知道每次跑步的最短距离是多少。

【输入格式】  

       输入的第1行包含两个整数:n和q,表示长跑路线有n个必经点,q次对路线的操作。接下来n行,每行2个整数,表示一个必经点的坐标,坐标范围在[-1000,1000]。再接下来q行,每行一个命令,格式有2种:  

       U i x y: 表示把第i个必经点的坐标改为(x, y)。  

       Q i j: 表示从父亲指定的子路线是必经点i到j的子路线,你需要回答小沐在该子路线上跑步的最短长度(可以省略一个必经点)。

【输出格式】  

       对每个Q命令,输出一个整数,表示子路线的最短长度。

【输入样例】

5 5

-4 4

-5 -3

-1 5

-3 4

0 5

Q 1 5

U 4 0 1

U 4 -1 1

Q 2 4

Q 1 4

【输出样例】

11

8

8

【数据范围】

       对于20%的数据满足:1 ≤ n,q ≤ 100

       对于50%的数据满足:1 ≤ n,q ≤ 1000

       对于100%的数据满足:1 ≤ n,q ≤ 100000


线段树…….这次是真的用来维护线段了。

其实二维平面没什么意义,我们完全可以把所有的必经点看做是一些一维数轴上的点,只不过点距需要用公式来特殊处理(曼哈顿距离),这样一转换,本题的方向就变得很明确了——就是单点修改+区间查询,妥妥的线段树。

某个点要变一下位置,带来的是点距的改变,我们用一个数组A[ ]来存储这些点距,其中A[i]表示必经点i到必经点i+1的距离,于是用A[1]….A[N-1]就把相邻点的点距存下来了。接下来就是用线段树来维护数组A[ ]了。

查询并不是那么常规,因为根据题目条件,我们在走一段必经点时(比如i->j),我们是可以忽略一个点的,那到底忽略哪个点能使距离最小呢?仿照上述思路,将忽略一个点后减小的距离同样地用一个数组B[ ]来存储,这样我们又可以拿线段树来维护了。

本题比较特殊的也是这一点,并不是直接拿线段树维护必经点的下标,而是维护距离,但直接维护距离显然是不可行的,所以我们拿数组来存储这些距离,再用线段树维护数组的下标。

可以说是很巧妙的一个技巧了。

另:初始化线段树、更新节点时,可能会访问到不符合要求的点,其实没什么关系,因为查询的时候我们并不会访问到这些点。



#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>#define ll long longusing namespace std;const int maxn=100000+5; const int inf =1e8;int n,q,rt,np;int a[maxn],b[maxn],lc[maxn*2],rc[maxn*2],maxv[maxn*2],sum[maxn*2];char op[1];struct data{int x;int y;}v[maxn];int calc1(int i,int j){return abs(v[i].x-v[j].x)+abs(v[i].y-v[j].y);}int calc2(int i){if(i==1 || i==n) return 0;return calc1(i-1,i)+calc1(i,i+1)-calc1(i-1,i+1);}void pushup(int now){sum[now]=sum[lc[now]]+sum[rc[now]];maxv[now]=max(maxv[lc[now]],maxv[rc[now]]);return;}void build(int &now,int l,int r){now=++np;if(l==r){sum[now]=calc1(l,l+1);        maxv[now]=calc2(l);return;}int m=(l+r)>>1;build(lc[now],l,m);build(rc[now],m+1,r);pushup(now);return;}void update(int now,int l,int r,int i,int k){if(l==i && r==i){if(k==1) sum[now]=calc1(i,i+1);if(k==2)maxv[now]=calc2(i);return;}int m=(l+r)>>1;if(i<=m)update(lc[now],l,m,i,k);else if(i>m)update(rc[now],m+1,r,i,k);pushup(now);return;}int query(int now,int l,int r,int i,int j,int k){if(i<=l && r<=j){if(k==1)return sum[now];if(k==2)return maxv[now];}int m=(l+r)>>1;if(j<=m)return query(lc[now],l,m,i,j,k);else if(i>m)return query(rc[now],m+1,r,i,j,k);else{int tl=query(lc[now],l,m,i,m,k);int tr=query(rc[now],m+1,r,m+1,j,k);if(k==1)return tl+tr;if(k==2) return max(tl,tr);}}void init(){scanf("%d%d",&n,&q);for(int i=1;i<=n;i++)scanf("%d%d",&v[i].x,&v[i].y);memset(lc,0,sizeof(lc));memset(rc,0,sizeof(rc));memset(sum,0,sizeof(sum));memset(maxv,0,sizeof(maxv));rt=np=0;build(rt,1,n);return;}void solve(){  int i,j,x,y;  for(int k=1;k<=q;k++)  {  scanf("%s",op);  if(op[0]=='U')  {  scanf("%d%d%d",&i,&x,&y);  v[i]=(data){x,y};  update(rt,1,n,i,1);  if(i!=1)update(rt,1,n,i-1,1);    update(rt,1,n,i,2);  if(i!=n) update(rt,1,n,i+1,2);  if(i!=1) update(rt,1,n,i-1,2);  }  else if(op[0]=='Q')  {  scanf("%d%d",&i,&j);  int ans;  if(i!=j) ans=query(rt,1,n,i,j-1,1);  else ans=0;   if(j-i>1) ans=ans-query(rt,1,n,i+1,j-1,2);cout<<ans<<"\n";   }  }  return;}int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);init();solve();return 0;}



原创粉丝点击