HDU 5044 Tree 树链剖分

Tree

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

【Problem Description】
   You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are numbered from 1 to N
   There are N - 1 edges numbered from 1 to N - 1.
   Each node has a value and each edge has a value. The initial value is 0.
   There are two kind of operation as follows:
   ●  ADD1 u v k: for nodes on the path from u to v, the value of these nodes increase by k.
   ●  ADD2 u v k: for edges on the path from u to v, the value of these edges increase by k.
   After finished M operation on the tree, please output the value of each node and edge.
 
【Input】
   The first line of the input is T (1 ≤ T ≤ 20), which stands for the number of test cases you need to solve.
   The first line of each case contains two integers N ,M (1 ≤ N, M ≤105),denoting the number of nodes and operations, respectively.
   The next N - 1 lines, each lines contains two integers u, v(1 ≤ u, v ≤ N ), denote there is an edge between u,v and its initial value is 0.
   For the next M line, contain instructions “ADD1 u v k” or “ADD2 u v k”. (1 ≤ u, v ≤ N, -105 ≤ k ≤ 105)
 
【Output】
   For each test case, print a line “Case #t:”(without quotes, t means the index of the test case) at the beginning.
   The second line contains N integer which means the value of each node.
   The third line contains N - 1 integer which means the value of each edge according to the input order.
 
【Sample Input】
2
4 2
1 2
2 3
2 4
ADD1 1 4 1
ADD2 3 4 2
4 2
1 2
2 3
1 4
ADD1 1 4 5
ADD2 3 2 4
【Sample Output】
Case #1:
1 1 0 1
0 2 2
Case #2:
5 0 0 5
0 4 0


【题意】

维护一棵树,操作是对某两点路径上的所有点的权值加上某个值,或者对某两点路径上的所有边的权值加上某个值。

【分析】

Kuang神出题的时候特别为这题卡了数据,LCT目测是不够过的,用树链剖分转成线性加输入挂也是勉强过,4700ms左右,那种1000~2000ms左右的算法不太明白是怎么写的。

1.维护两点路径上的点权值是树链剖分的基本操作

2.维护两点路径上的边权值需要在剖分的时候直接按照边的情况,直接用子结点的序号来代表一条边,然后做好边原本序号的记录

一般的树链剖分是直接用树状数组,但是由于本题的最终结果要求的是输出所有点和所有边的权值,因此采用树状数组每次求一次sum来得到每个点的值就有点浪费效率了。

可以参考原本树状数组的方式,直接用一个普通的数组来记录情况,在起点+value,在终点后的一个点-value,表示对整段线段完成加权值;最终求和的时候只需要从头向后扫一遍,求出每个点的sum(i)即可

  1 /* ***********************************************
  2 MYID    : Chen Fan
  3 LANG    : G++
  4 PROG    : HDU 5044
  5 ************************************************ */
  6 
  7 #include <iostream>
  8 #include <cstdio>
  9 #include <cstring>
 10 #include <algorithm>
 11 
 12 using namespace std;
 13 
 14 #define MAXN (int)1E5+10
 15 #define MAXM (int)2E5+10
 16 
 17 int son[MAXN],ans[MAXN];
 18 int father[MAXN],size[MAXN],level[MAXN],data[MAXN],top[MAXN];
 19 int start[MAXN];
 20 
 21 typedef struct nod
 22 {
 23     int to,next,no;
 24 } node;
 25 node edge[MAXM];
 26 int last,n;
 27 
 28 void addedge(int from,int to,int k)
 29 {
 30     last++;
 31     edge[last].to=to; 
 32     edge[last].next=start[from];
 33     edge[last].no=k;
 34     start[from]=last;
 35 }
 36 
 37 int c[MAXN],pos[MAXN],fp[MAXN];
 38 int ec[MAXN],epos[MAXN],fep[MAXN];
 39 int tot;
 40 
 41 int q[MAXN];
 42 void bfs()
 43 {
 44     int head=1,tail=1;
 45     q[1]=1;
 46     level[1]=0;
 47     father[1]=0;
 48 
 49     while(head<=tail)
 50     {
 51         int now=q[head];
 52         size[now]=1;
 53         for (int i=start[now];i;i=edge[i].next)
 54         {
 55             int temp=edge[i].to;
 56             if (temp!=father[now])
 57             {
 58                 father[temp]=now;
 59                 fep[temp]=edge[i].no;
 60                 level[temp]=level[now]+1;
 61                 tail++;
 62                 q[tail]=temp;
 63             }
 64         }
 65         head++;
 66     }
 67     for (int i=n;i>=1;i--)
 68     {
 69         int now=q[i];
 70         if (father[now])
 71         {
 72             size[father[now]]+=size[now];
 73             if (son[father[now]]==0||size[now]>size[son[father[now]]])
 74             son[father[now]]=now;
 75         }
 76     }
 77     for (int i=1;i<=n;i++)
 78     {
 79         int now=q[i];
 80         if (son[father[now]]==now) top[now]=top[father[now]];
 81         else 
 82         {
 83             top[now]=now;
 84             while(now)
 85             {
 86                 tot++;
 87                 pos[now]=tot;
 88                 fp[tot]=now;
 89                 now=son[now];
 90             }
 91         }
 92     }
 93 }
 94 
 95 void change(int x,int y,int value)
 96 {
 97     while(top[x]!=top[y])
 98     {
 99         if (level[top[x]]<level[top[y]]) swap(x,y);
100     
101         c[pos[top[x]]]+=value;
102         c[pos[x]+1]-=value;
103 
104         x=father[top[x]];
105     }
106     if (level[x]>level[y]) swap(x,y);
107     
108     c[pos[x]]+=value;
109     c[pos[y]+1]-=value;
110 }
111 
112 void change_edge(int x,int y,int value)
113 {
114     while(top[x]!=top[y])
115     {
116         if (level[top[x]]<level[top[y]]) swap(x,y);
117     
118         ec[pos[top[x]]]+=value;
119         ec[pos[x]+1]-=value;
120 
121         x=father[top[x]];
122     }
123     if (level[x]>level[y]) swap(x,y);
124     
125     ec[pos[son[x]]]+=value;
126     ec[pos[y]+1]-=value;
127 }
128 
129 int INT() {
130     char ch;
131     int res;
132     bool neg;
133     while (ch = getchar(), !isdigit(ch) && ch != '-')
134         ;
135     if (ch == '-') {
136         neg = true;
137         res = 0;
138     } else {
139         neg = false;
140         res = ch - '0';
141     }
142     while (ch = getchar(), isdigit(ch))
143         res = res * 10 + ch - '0';
144     return neg ? -res : res;
145 }
146 
147 int main()
148 {
149     freopen("5044.txt","r",stdin);
150 
151     int t;
152     t=INT();
153 
154     for (int tt=1;tt<=t;tt++)
155     {
156         printf("Case #%d:
",tt);
157 
158         int m;
159         n=INT();m=INT();
160     
161         memset(start,0,sizeof(start));
162         last=0;
163         for (int i=1;i<n;i++)
164         {
165             int u,v;
166             u=INT();
167             v=INT();
168             addedge(u,v,i);
169             addedge(v,u,i);
170         }
171 
172         memset(son,0,sizeof(son));
173         tot=0;
174         bfs();
175 
176         memset(c,0,sizeof(c));
177         memset(ec,0,sizeof(ec));
178 
179         for (int i=1;i<=m;i++)
180         {
181             char s;
182             int c1,c2,k;
183             s=getchar();
184             while (!isdigit(s)) s=getchar();
185             c1=INT();
186             c2=INT();
187             k=INT();
188             if (s=='1') change(c1,c2,k);
189             else change_edge(c1,c2,k);
190         }
191 
192         int ss=0;
193         for (int i=1;i<=n;i++)
194         {
195             ss+=c[i];
196             ans[fp[i]]=ss;
197         }
198         printf("%d",ans[1]);
199         for (int i=2;i<=n;i++) 
200         {
201             putchar(' ');
202             printf("%d",ans[i]);
203         }
204         putchar('
');
205 
206         ss=0;
207         for (int i=1;i<=n;i++)
208         {
209             ss+=ec[i];
210             ans[fep[fp[i]]]=ss;
211         }
212         if (n>1) printf("%d",ans[1]);
213         for (int i=2;i<n;i++) 
214         {
215             putchar(' ');
216             printf("%d",ans[i]);
217         }
218         putchar('
');
219         
220     }
221 
222     return 0;
223 }
View Code
原文地址:https://www.cnblogs.com/jcf94/p/4488801.html