HDU 3966 dfs序+LCA+树状数组

题目意思很明白:

给你一棵有n个节点的树,对树有下列操作:

I c1 c2 k 意思是把从c1节点到c2节点路径上的点权值加上k

D c1 c2 k 意思是把从c1节点到c2节点路径上的点权值减去k

Q a 查询节点a的权值

数据大小 节点个数 n[1,50000], 操作次数 op[0,30000];

不会树链剖分 故只有想其他的方法。

这道题有点类似今年上海网络赛的1003 ,不过那题我没做;

算法思路:

以节点1 为根,求出节点i 的 dfs序列 tim[i][2];

其中tim[i][0]存的是进入节点i的时间 ,tim[i][1]存的是离开节点i的时间

看操作: I a b k, 节点a到b的路径上所有点+k;最朴素的算法首先找到他们的最近公共祖先(LCA)(我用RMQ)写的;然后在路径上的每一个节点都+k;

假设树根是1

从a到树根的每一个节点x都满足tim[x][0]<=tim[a][0]<=tim[x][1];如此 如果引入一个数列nt,如果nt(tim[a][0])+=k,那么 nt数列从tim[x][0]到tim[x][1]项的和就加了k,这就相当于把从a到根节点的每一个节点的权值加上了k;

如果节点x不在a到根的路径上呢?那么只有可能是tim[x][0]>tim[a][0] or tim[x][1]<tim[a][0]  故上面的这种做法对其他的点没有影响

根据这种思路 ,那解法就相对简单了:

加上差分数列的思想

a b的LCA是t,把 a b节点都加上k,然后把t的父节点-2;当然 t节点是加了两遍的 所以需要减去一遍 也就是把t节点-1,父节点当然也要+1了

如果有了dfs序,就可以很清晰的看出来 如果一个节点x在这条路径上 那么tim[x][0]<=one of (tim[a][0],tim[b][0])<=tim[x][1];

附上代码渣渣:

  1 // hdu 3966
  2 #include<iostream>
  3 #include<stdio.h>
  4 #include<string.h>
  5 #include <string>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <map>
  9 #include <set>
 10 #include <queue>
 11 #include <stack>
 12 #include<stdlib.h>
 13 #include <vector>
 14 using namespace std;
 15
 16 #define ll __int64
 17 #define CL(a,b) memset(a,b,sizeof(a))
 18 #define MAX_NODE 50010
 19
 20 int n,m,q;
 21
 22 int log_2(int x)
 23 {
 24     return (int)(log((double)x)/log(2.0));
 25 }
 26
 27 int tim[MAX_NODE][2],ti;
 28 int rmq[MAX_NODE*2][30],cq,fv[MAX_NODE];
 29 int pre[MAX_NODE];
 30 int value[50010];
 31
 32 typedef struct myedge
 33 {
 34     int v,next;
 35 }E;
 36
 37 E edge[100010];
 38 int head[50010],ce;
 39
 40 void inithead()
 41 {
 42     CL(head,-1);
 43     ce=0;
 44 }
 45 void addedge(int s,int e)
 46 {
 47     edge[ce].v=e;edge[ce].next=head[s];head[s]=ce++;
 48     edge[ce].v=s;edge[ce].next=head[e];head[e]=ce++;
 49 }
 50
 51 void initrmq()
 52 {
 53     int i,j;
 54     for(j=1;(1<<j)<=cq;j++)
 55     {
 56         for(i=1;i+(1<<j)-1<=cq;i++)
 57         {
 58             rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
 59         }
 60     }
 61 }
 62 int quermq(int s,int e)
 63 {
 64     int i=log_2(e-s+1);
 65     return min(rmq[s][i],rmq[e-(1<<i)+1][i]);
 66 }
 67
 68 ll nt[50010];
 69
 70 int lowbit(int i)
 71 {
 72     return i&(-i);
 73 }
 74
 75 void modify(int i,ll v)
 76 {
 77     if(i==0)return ;
 78     for(i;i<=n;i+=lowbit(i))
 79     {
 80         nt[i]+=v;
 81     }
 82 }
 83
 84 ll sum(int i)
 85 {
 86     ll rem=0;
 87     for(i;i>0;i-=lowbit(i))
 88         rem+=nt[i];
 89     return rem;
 90 }
 91
 92 int dfs(int i,int pr)
 93 {
 94     ti++;
 95     tim[i][0]=ti;
 96 //    cout<<i<<" tim "<<ti<<endl;
 97     rmq[++cq][0]=tim[i][0];
 98     fv[i]=cq;
 99     pre[ti]=pr;
100     int p=head[i];
101     while(p!=-1)
102     {
103         int v=edge[p].v;
104         if(tim[v][0]==0)
105         {
106             dfs(v,tim[i][0]);
107             rmq[++cq][0]=tim[i][0];
108         }
109         p=edge[p].next;
110     }
111     tim[i][1]=ti;
112     return 0;
113 }
114
115 void inc(int s,int e,int k)
116 {
117     int rs=fv[s];
118     int re=fv[e];
119     int rt=quermq(min(rs,re),max(rs,re));
120     re=tim[e][0];
121     rs=tim[s][0];
122    // cout<<rs<<‘ ‘<<re<<‘ ‘<<rt<<‘ ‘<<pre[rt]<<endl;
123     if(s==e)
124     {
125         value[s]+=k;
126         return ;
127     }
128     modify(rs,k);
129     modify(re,k);
130     modify(rt,-k);
131     modify(pre[rt],-k);
132 }
133
134 void dec(int s,int e,int k)
135 {
136     k=-k;
137     int rs=fv[s];
138     int re=fv[e];
139     int rt=quermq(min(rs,re),max(rs,re));
140     re=tim[e][0];
141     rs=tim[s][0];
142     if(s==e)
143     {
144         value[s]+=k;
145         return ;
146     }
147     modify(rs,k);
148     modify(re,k);
149     modify(rt,-k);
150     modify(pre[rt],-k);
151 }
152
153 ll que(int k)
154 {
155     int rl=tim[k][0];
156     int rr=tim[k][1];
157  //   cout<<rl<<‘ ‘<<rr<<endl;
158     return (ll)value[k]+sum(rr)-sum(rl-1);
159 }
160
161 char op[3];
162
163
164 int main()
165 {
166     while(scanf("%d %d %d",&n,&m,&q)!=EOF)
167     {
168         CL(nt,0);
169         CL(rmq,0);cq=0;
170         CL(fv,0);
171         CL(pre,0);
172         CL(tim,0);ti=0;
173         inithead();
174         int i,j,k;
175         int a,b,c;
176         for(i=1;i<=n;i++)
177         {
178             scanf("%d",value+i);
179         }
180         for(i=1;i<n;i++)
181         {
182             scanf("%d %d",&a,&b);
183             addedge(a,b);
184         }
185         dfs(1,0);
186 /*
187         for(i=1;i<=cq;i++)
188         {
189             printf("%d ",rmq[i][0]);
190         }cout<<endl;
191 */
192         initrmq();
193         for(i=0;i<q;i++)
194         {
195             scanf("%s",op);
196             if(op[0]==‘I‘)
197             {
198                 scanf("%d %d %d",&a,&b,&c);
199                 inc(a,b,c);
200             }
201             else if(op[0]==‘D‘)
202             {
203                 scanf("%d %d %d",&a,&b,&c);
204                 dec(a,b,c);
205             }
206             else if(op[0]==‘Q‘)
207             {
208                 scanf("%d",&a);
209                 printf("%I64d\n",que(a));
210             }
211         }
212
213     }
214
215     return 0;
216 }
时间: 2024-10-05 13:25:34

HDU 3966 dfs序+LCA+树状数组的相关文章

HDU 6203 ping ping ping(dfs序+LCA+树状数组)

http://acm.hdu.edu.cn/showproblem.php?pid=6203 题意: n+1 个点 n 条边的树(点标号 0 ~ n),有若干个点无法通行,导致 p 组 U V 无法连通.问无法通行的点最少有多少个. 思路: 贪心思维,破坏两个点的LCA是最佳的.那么怎么判断现在在(u,v)之间的路径上有没有被破坏的点呢,如果没有的话那么此时就要破坏这个lca点.一开始我们要把询问按照u和v的lca深度从大到小排序,如果某个点需要被破坏,那么它的所有子节点都可以不再需要破坏别的点

POJ 2763 Housewife Wind(DFS序+LCA+树状数组)

Housewife Wind Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 11419   Accepted: 3140 Description After their royal wedding, Jiajia and Wind hid away in XX Village, to enjoy their ordinary happy life. People in XX Village lived in beauti

BZOJ 2819 Nim 树链剖分/DFS序+LCA+树状数组

题意:给定一棵树,每个节点是一堆石子,给定两种操作: 1.改变x号节点的石子数量 2.用从x到y的路径上的所有堆石子玩一次Nim游戏,询问是否有必胜策略 Nim游戏有必胜策略的充要条件是所有堆的石子数异或起来不为零 这题首先一看就是树链剖分 然后题目很善良地告诉我们深搜会爆栈 于是我们可以选择广搜版的树链剖分 BFS序从左到右是深搜,从右到左是回溯,一遍BFS就够 单点修改区间查询还可以套用ZKW线段树 不过这题其实不用这么麻烦 有更简单的办法 详见 http://dzy493941464.is

HDU 5156 - Harry and Christmas tree (dfs序+离线树状数组)

http://acm.hdu.edu.cn/showproblem.php?pid=5156 BC#25的C题. 题意是:给出一颗大小为n的树,以1为根,然后给出m次染色,每次将节点u加上一种颜色(一个节点可以有多个颜色). 最后查询树上每个节点对应子树上包含的不同颜色数量. 当时这场比赛没有做,回来看一下题目,没看标解就试着敲了一遍,于是解题思路从一开始就走上了不归路. 标解是O(n+m)的方法,主要思路是将问题转为:一次染色表示将u到根节点的路径都染上这种颜色. 但这样做需要去重,因为如果u

【BZOJ1103】大都市meg(DFS序,树状数组)

题意:有一颗树,1号点为根,保证编号小的点深度较小,初始状态每条边都没有被标记,要求实现两个操作在线: A:将连接x,y的边标记 W:查询从1到x的路径上有多少条边未被标记 n<=2*10^5 思路:本题的特殊性质: 1.一次只标记一条边且没有重边 2.直接求1到x的路径,不用LCA 记录i点在DFS序中第一次与最后一次出现的时间b[i]与c[i] 可以发现若(x,y)(x<y)边被标记只对区间(b[y],c[y])有1的贡献 等价于前缀和s[b[y]]++ s[c[y]+1]-- 至于s[c

【Tyvj2133 BZOJ1146】网络管理Network(树套树,DFS序,树状数组,主席树,树上差分)

题意:有一棵N个点的树,每个点有一个点权a[i],要求在线实现以下操作: 1:将X号点的点权修改为Y 2:查询X到Y的路径上第K大的点权 n,q<=80000 a[i]<=10^8 思路:此题明显地体现了我对主席树理解不深 树上路径K大可以直接用树剖+二分答案+树做 但DFS序+主席树也可以 对于点U,它能影响DFS序上的区间(st[u],ed[u]) 所以维护方法就是类似序列K大一样 s[st[u]]++ s[ed[u]+1]-- 对于路径(x,y),信息为s[x]+s[y]-s[lca(x

HDU 5296 Annoying problem LCA+树状数组

题解链接 Annoying problem Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 480    Accepted Submission(s): 146 Problem Description Coco has a tree, whose nodes are conveniently labeled by 1,2,-,n, w

HDOJ5877(dfs序+离散化+树状数组)

Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 2081    Accepted Submission(s): 643 Problem Description You are given a rooted tree of N nodes, labeled from 1 to N. To the ith node a

CF983E NN country [倍增][LCA][树状数组]

题意: $n$个城市,从$1$到$n$标号,$n$个城市构成一棵树. 有$m$条双向公交路线,对于每条路线,公交沿着两个终点站之间的最短路径行驶并会在沿途各站停车.从一个城市只能坐公交前往其他城市. 有$q$个询问:从一个城市到另一个城市要搭乘多少趟公交?不能到达输出$-1$. 对于每个询问$x,y$,求出$z=lca(x,y)$. 先从$x$和$y$出发到达$z$下方的城市$x'$和$y'$使得再坐一趟车可到$z$,记步数和为$s$.倍增,预处理$f[i][j]$表示从$i$出发坐$2^{j}