FZU2176---easy problem (树链剖分)
Accept: 9 Submit: 32
Time Limit: 2000 mSec Memory Limit : 32768 KB
Problem Description
给定一棵n个节点以1为根的树,初始每个节点的值为0,现在我们要在树上进行一些操作,操作有两种类型。
1 x val 表示对以x为根的子树的每个点进行加权操作(我们定义每个节点的深度为每个节点到根1的距离),如果 y是以x为根的子树中的点那么 y节点的权值增加 ((dep[y]-dep[x])%k+1)*val 其中dep[y]表示y节点的深度,k为一个常数(1<=k<=5)
2 x 查询当前x节点的权值。
Input
首先输入一个整数T 表示数据的组数,每组数据 第一行有3个整数 n ,m, k(1<=n,m<=50000, 1<=k<=5) 。n表示节点数,m表示操作数,k表示如题目描述的常数。
接下去n-1行描述一棵树。接下去m行每行表示 一个操作 有两种类型1 x val 表示第一种类型 2 x 表示第二种类型。(1<=x<=n,1<=val<=100)
Output
每组数据首先输出 Case#x: 表示第x组数据 x从1开始。接下去对于每组数据中的每个查询操作输出查询结果。(具体输出格式看样例)
Sample Input
1
5 7 2
1 2
2 4
2 5
1 3
1 2 3
1 1 2
2 1
2 2
2 3
2 4
2 5
Sample Output
Case#1:
2
7
4
8
8
大致思路:首先树链剖分一下,然后用树状数组或者线段树维护区间更新,但是((dep[y]-dep[x])%k+1)*val这种该怎么更新呢,,看到k很小最大只有5,可以想到用5棵线段树或者树状数组来维护。首先每个节点的深度dep确定后 每隔k个深度权值增加的是相等的。这样的话 就可以把所有的点对应到 k个树状数组 上去。然后就是树状数组经典的区间更新单点查询操作了。
1 #include <set> 2 #include <map> 3 #include <cmath> 4 #include <ctime> 5 #include <queue> 6 #include <stack> 7 #include <cstdio> 8 #include <string> 9 #include <vector> 10 #include <cstdlib> 11 #include <cstring> 12 #include <iostream> 13 #include <algorithm> 14 using namespace std; 15 typedef unsigned long long ull; 16 typedef long long ll; 17 const int inf = 0x3f3f3f3f; 18 const double eps = 1e-8; 19 const int maxn = 5e4+10; 20 struct 21 { 22 int to,next; 23 } e[maxn<<1]; 24 int tot,head[maxn]; 25 void add_edge(int x,int y) 26 { 27 e[tot].to = y; 28 e[tot].next = head[x]; 29 head[x] = tot++; 30 } 31 int siz[maxn],son[maxn],fa[maxn],dep[maxn]; 32 void dfs(int root) 33 { 34 siz[root] = 1; 35 son[root] = 0; 36 for (int i = head[root]; ~i; i = e[i].next) 37 { 38 if (fa[root] != e[i].to) 39 { 40 fa[e[i].to] = root; 41 dep[e[i].to] = dep[root] + 1; 42 dfs(e[i].to); 43 if (siz[e[i].to] > siz[son[root]]) 44 son[root] = e[i].to; 45 siz[root] += siz[e[i].to]; 46 } 47 } 48 } 49 int pos[maxn],top[maxn],L[maxn],R[maxn],bj1,bj2,idx; 50 void build (int root,int father) 51 { 52 pos[root] = ++idx; 53 top[root] = father; 54 L[root] = root; 55 R[root] = root; 56 if (son[root] > 0) 57 { 58 bj1 = son[root]; 59 build(son[root],top[root]); 60 L[root] = bj1; 61 } 62 bool flag = 0; 63 for (int i = head[root]; ~i; i = e[i].next) 64 { 65 flag = 1; 66 if (fa[root] != e[i].to && son[root] != e[i].to) 67 bj2 = e[i].to,build(e[i].to,e[i].to); 68 else if (siz[root] == 1) 69 bj2 = root; 70 } 71 if (flag) 72 R[root] = bj2; 73 } 74 int lowbit(int x) 75 { 76 return x & -x; 77 } 78 int c[6][maxn],n; 79 void add(int x,int d,int k) 80 { 81 while (x <= n) 82 { 83 c[k][x] += d; 84 x += lowbit(x); 85 } 86 } 87 int sum(int x,int k) 88 { 89 int ans = 0; 90 while (x > 0) 91 { 92 ans += c[k][x]; 93 x -= lowbit(x); 94 } 95 return ans; 96 } 97 void init() 98 { 99 int root = 1; 100 tot = idx = 0; 101 dep[root] = fa[root] = 0; 102 memset(head,-1,sizeof(head)); 103 memset(siz,0,sizeof(siz)); 104 memset(c,0,sizeof(c)); 105 for (int i = 1; i < n; i++) 106 { 107 int u,v; 108 scanf ("%d%d",&u,&v); 109 add_edge(u,v); 110 add_edge(v,u); 111 } 112 dfs(root); 113 build(root,root); 114 } 115 int main(void) 116 { 117 #ifndef ONLINE_JUDGE 118 freopen("in.txt","r",stdin); 119 #endif 120 int t,cas = 1; 121 scanf ("%d",&t); 122 while (t--) 123 { 124 printf("Case#%d:\n",cas++); 125 int k,m; 126 scanf ("%d%d%d",&n,&m,&k); 127 init(); 128 for (int i = 0; i < m; i++) 129 { 130 int op,x,val; 131 scanf ("%d%d",&op,&x); 132 if (op == 1) 133 { 134 scanf ("%d",&val); 135 for (int j = 0; j < k; j++) 136 { 137 int tmp = (j + 1) * val; 138 int l = pos[x]; 139 int r = pos[R[x]]; 140 add(pos[x], tmp,((dep[x] + 1)%k + j)%k); 141 add(pos[R[x]] + 1, -tmp,((dep[x] + 1)%k + j)%k); 142 } 143 } 144 if (op == 2) 145 { 146 printf("%d\n",sum(pos[x],(dep[x]-dep[1] + 1) % k)); 147 } 148 } 149 } 150 return 0; 151 }