Stanford Local 2016 G "Ground Defense"(线段树)
题意:
有 n 个城市,编号 1~n;
有两种操作:Update,Query
Update:
E i s a d
更新区间[ i,i+d-1 ], i 节点降落 s 人, i+1 节点降落 s+a 人, i+2 节点降落 s+2*a 人,......, i+d-1 节点降落 s+(d-1)*a 人;
W i s a d
更新区间[ i-d+1,i ], i 节点降落 s 人, i-1 节点降落 s+a 人, i-2 节点降落 s+2*a 人,......, i-d+1 节点降落 s+(d-1)*a 人;
简言之,从 i 节点下降 s 人开始,向东(右),西(左)下降的人数依次 +a;
Query: i
查询在节点 i 降落的总人数。
题解:
看到这道题,第一反应是线段树区间更新,那具体怎么个更新法呢?
首先看线段树中定义的元素:
struct SegmentTree { int l,r; ll s;//l处增加s人 ll a;//从l到r依次变化a人 int mid() { return l+((r-l)>>1); } }segTree[4*maxn];
定义Update()函数,具体如下:
void Update(int l,int r,int pos,ll s,ll a) { if(segTree[pos].l == l && segTree[pos].r == r) { segTree[pos].s += s;//pos节点中的s增加s segTree[pos].a += a;//pos节点中的a增加a return ; } pushDown(pos);//向下更新 int mid=segTree[pos].mid(); if(r <= mid) Update(l,r,ls(pos),s,a); else if(l > mid) Update(l,r,rs(pos),s,a); else { ll d=mid+1-l; Update(l,mid,ls(pos),s,a); Update(mid+1,r,rs(pos),s+d*a,a);//注意右儿子更新的s值 } }
如果更新操作为 E i s a d :
调用函数 Update( i , i+d-1 , 1 , s , a );
反之,如果更新操作为 W i s a d ⇔ E (i-d+1) ( s+(d-1)*a ) (-a) d
调用函数 Update( (i-d+1) , i , 1 , s+(d-1)*a , -a );
更新函数中的pushDown()函数是非常重要的,其决定了此算法的正确与否:
//将pos节点更新的状态传给儿子节点 void pushDown(int pos) { ll &s=segTree[pos].s; ll &a=segTree[pos].a; if(s)//如果s不为0,说明在这之前曾更新过pos节点 { //左儿子的更新 segTree[ls(pos)].s += s; segTree[ls(pos)].a += a; /** 右儿子的更新操作 注意 segTree[rs(pos)].s 增加的值 具体原因留给读者自己思索 */ segTree[rs(pos)].s += s+a*(segTree[rs(pos)].l-segTree[ls(pos)].l); segTree[rs(pos)].a += a; } s=0; a=0; }
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define ls(x) (x<<1) 7 #define rs(x) (x<<1|1) 8 #define ll long long 9 #define mem(a,b) memset(a,b,sizeof(a)) 10 const int maxn=5e5+50; 11 12 int n,m; 13 struct SegmentTree 14 { 15 int l,r; 16 ll s;//l处增加s人 17 ll a;//从l到r依次增加a 18 int mid() 19 { 20 return l+((r-l)>>1); 21 } 22 }segTree[4*maxn]; 23 24 void pushDown(int pos) 25 { 26 ll &s=segTree[pos].s; 27 ll &a=segTree[pos].a; 28 if(s) 29 { 30 segTree[ls(pos)].s += s; 31 segTree[ls(pos)].a += a; 32 33 /** 34 右儿子的更新操作 35 注意 segTree[rs(pos)].s 增加的值 36 具体原因留给读者自己思索 37 */ 38 segTree[rs(pos)].s += s+a*(segTree[rs(pos)].l-segTree[ls(pos)].l); 39 segTree[rs(pos)].a += a; 40 } 41 s=0; 42 a=0; 43 } 44 void buildSegTree(int l,int r,int pos) 45 { 46 segTree[pos].l=l; 47 segTree[pos].r=r; 48 segTree[pos].s=0; 49 segTree[pos].a=0; 50 if(l == r) 51 return ; 52 53 int mid=l+((r-l)>>1); 54 buildSegTree(l,mid,ls(pos)); 55 buildSegTree(mid+1,r,rs(pos)); 56 } 57 void Update(int l,int r,int pos,ll s,ll a) 58 { 59 if(segTree[pos].l == l && segTree[pos].r == r) 60 { 61 segTree[pos].s += s; 62 segTree[pos].a += a; 63 return ; 64 } 65 pushDown(pos);//向下更新 66 67 int mid=segTree[pos].mid(); 68 if(r <= mid) 69 Update(l,r,ls(pos),s,a); 70 else if(l > mid) 71 Update(l,r,rs(pos),s,a); 72 else 73 { 74 ll d=mid+1-l; 75 Update(l,mid,ls(pos),s,a); 76 Update(mid+1,r,rs(pos),s+d*a,a);//注意右儿子更新的s值 77 } 78 } 79 ll Query(int x,int pos) 80 { 81 if(segTree[pos].l == segTree[pos].r) 82 return segTree[pos].s; 83 pushDown(pos); 84 85 int mid=segTree[pos].mid(); 86 if(x <= mid) 87 return Query(x,ls(pos)); 88 else 89 return Query(x,rs(pos)); 90 } 91 int main() 92 { 93 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin); 94 int test; 95 scanf("%d",&test); 96 while(test--) 97 { 98 scanf("%d%d",&m,&n); 99 buildSegTree(1,n,1); 100 while(m--) 101 { 102 char order[2]; 103 scanf("%s",order); 104 if(order[0] == 'U') 105 { 106 char x[2]; 107 int i,s,a,d; 108 scanf("%s%d%d%d%d",x,&i,&s,&a,&d); 109 if(x[0] == 'E') 110 { 111 //r=min()是为了防止数据出错使得 i+d-1 > n 112 int r=min(n,i+d-1); 113 Update(i,r,1,s,a); 114 } 115 else 116 { 117 //l=max()是为了防止数据出错使得 i-d+1 < 1 118 int l=max(1,i-d+1); 119 Update(l,i,1,1ll*s+1ll*a*(d-1),-a); 120 } 121 } 122 else 123 { 124 int i; 125 scanf("%d",&i); 126 printf("%lld\n",Query(i,1)); 127 } 128 } 129 } 130 return 0; 131 }