[LNOI2014]LCA

~~~题面~~~

题解:

  首先我们要转化一下,因为直接求不好求。首先考虑一个点对z的贡献,观察这么一个图:

  显然点x对点z的贡献为2,因为LCA的深度为2。LCA可以看做点x和点z分别走向root的两条路径中第一个重合的点,因此,如果我们给x到root的路径上的点都赋1的点权,那么再从z往上走,

  因为LCA是两条路径中第一个重合的点,因此我们会从LCA开始获取点权,那么走到root后得到的点权和刚好就是点x对点z的贡献!

  因此我们直接做一个树链剖分,然后用线段树来维护区间加和区间查询即可。

  然而直接对每个询问都这么做显然是会超时的,因此我们要考虑一些效率更高的做法。

  因为每个询问都是一个区间的查询,而且符合区间减法,即对于同一点而言ans[1, r] - ans[1, l -1] = ans[l, r]。

  这时思路就很明确了,我们可以从1开始枚举,依次进行区间加,然后每次加了之后就处理一遍相关的询问,跟tarjan求LCA的思想有一点点类似。

  获取了ans[1, r] 和 ans[1, l - 1]后就可以很方便的求出ans[l, r]了。


  

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 50100
  5 #define ac 500100
  6 #define mod 201314
  7 #define getchar() *o++
  8 char READ[5000100], *o = READ;
  9 int n, m, cnt, rnt;
 10 int Head[AC], Next[AC], date[AC], all;
 11 int Size[AC], son[AC], father[AC], dep[AC], ans[AC], top[AC], id[AC], d[AC], last[AC];
 12 
 13 struct node{
 14     int Head[AC], Next[AC*2], date[AC*2], id[AC*2], tot;//error!!!询问会存2次啊,左端点和右端点啊,,,数组开两倍啊!!!
 15     void add(int f, int w, int S)//存询问
 16     {
 17         date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, id[tot] = S;
 18     }
 19 }E;
 20 
 21 struct _question{
 22     int l, r, x;
 23 }q[AC];
 24 
 25 inline void add(int f, int w)
 26 {
 27     date[++all] = w, Next[all] = Head[f], Head[f] = all;
 28 }
 29 
 30 inline int read()
 31 {
 32     int x = 0;char c = getchar();
 33     while(c > '9' || c < '0') c = getchar();
 34     while(c >= '0' && c <= '9') x = x * 10 + c -'0', c = getchar();
 35     return x;
 36 }
 37 
 38 void pre()
 39 {
 40     int a;
 41     n = read(), m = read();
 42     for(R i = 2; i <= n; i++)
 43     {
 44         a = read() + 1;//编号从移到1开始算
 45         add(a, i);
 46         father[i] = a;
 47     }
 48     for(R i = 1; i <= m; i++)
 49     {
 50         q[i].l = read() + 1, q[i].r = read() + 1, q[i].x = read() + 1;
 51         if(q[i].l > q[i].r) swap(q[i].l, q[i].r);
 52         E.add(q[i].l - 1, q[i].x, i);//从前向后枚举点x差分,这样就会先遇到左端点,
 53         E.add(q[i].r, q[i].x, i);//所以遇到右端点查询的时候就直接减就可以了
 54     }
 55     dep[1] = 1;
 56 }
 57 
 58 void dfs1(int x)//get Size, son, dep
 59 {
 60     int now, maxn = 0;
 61     Size[x] = 1;//先加上自己
 62     for(R i = Head[x]; i; i = Next[i])
 63     {
 64         now = date[i];
 65         if(now == father[x]) continue;
 66         dep[now] = dep[x] + 1;
 67         dfs1(now);
 68         Size[x] += Size[now];
 69         if(Size[now] > Size[maxn]) maxn = now;
 70     }
 71     son[x] = maxn;
 72 }
 73 
 74 void dfs2(int x, int topx)//get id, top
 75 {
 76     int now;
 77     id[x] = ++cnt, top[x] = topx;
 78     if(!son[x]) return ;
 79     dfs2(son[x], topx);//重链需要继承
 80     last[x] = son[x];
 81     for(R i = Head[x]; i; i = Next[i])
 82     {
 83         now = date[i];
 84         if(now == father[x] || now == son[x]) continue;//如果是父亲or重儿子就跳过
 85         dfs2(now, now);//新开链
 86     }
 87 }
 88 
 89 int tree[ac], lazy[ac], l[ac], r[ac];
 90 
 91 void build(int x, int ll, int rr)
 92 {
 93     if(ll == rr)
 94     {
 95         l[x] = r[x] = ll;
 96         return ;
 97     }
 98     l[x] = ll, r[x] = rr;
 99     int mid = (ll + rr) >> 1;
100     build(x * 2, ll, mid);
101     build(x * 2 + 1, mid + 1, rr);
102 
103 }
104 
105 inline void pushdown(int x)
106 {
107     if(lazy[x])
108     {
109         int ll = x * 2, rr = ll + 1;
110         lazy[ll] += lazy[x], lazy[rr] += lazy[x];
111         tree[ll] += lazy[x] * (r[ll] - l[ll] + 1) , tree[rr] += lazy[x] * (r[rr] - l[rr] + 1);
112         lazy[x] = 0;
113     }
114 }
115 
116 inline void update(int x)
117 {
118     tree[x] = tree[x * 2] + tree[x * 2 + 1];
119 }
120 
121 void add(int x, int ll, int rr)//区间加
122 {
123     pushdown(x);
124     if(l[x] == ll && r[x] == rr)
125     {
126         tree[x] += (rr - ll + 1);
127         lazy[x] += 1;
128         return ;
129     }
130     int mid = (l[x] + r[x]) >> 1;
131     if(rr <= mid) add(x * 2, ll, rr);
132     else if(ll > mid) add(x * 2 + 1, ll, rr);
133     else 
134     {
135         add(x * 2, ll, mid);
136         add(x * 2 + 1, mid + 1, rr);
137     }
138     update(x);
139 }
140 
141 void search(int x, int ll, int rr)//查询
142 {
143     pushdown(x);
144     if(l[x] == ll && r[x] == rr)
145     {
146         rnt += tree[x];
147         return ;
148     }
149     int mid = (l[x] + r[x]) >> 1;
150     if(rr <= mid) search(x * 2, ll, rr);
151     else if(ll > mid) search(x * 2 + 1, ll, rr);
152     else 
153     {
154         search(x * 2, ll, mid);
155         search(x * 2 + 1, mid + 1, rr);
156     }    
157 }
158 
159 void change(int x)
160 {
161     while(x)
162     {
163         add(1, id[top[x]], id[x]);
164         x = father[top[x]];
165     }
166     if(x == 1) add(1, 1, 1);//如果不是0就会跳过1
167 
168 }
169 
170 int find(int x)
171 {
172     rnt = 0;
173     while(x)
174     {
175         search(1, id[top[x]], id[x]);
176         x = father[top[x]];
177     }
178     if(x == 1) search(1, 1, 1); 
179     return rnt;
180 }
181 
182 void work()
183 {
184     int x;
185     for(R i = 1; i <= n; i++)
186     {
187         change(i);//修改i ---> root
188         for(R j = E.Head[i]; j; j = E.Next[j])
189         {
190             x = E.date[j];//获取z
191             ans[E.id[j]] = (find(x) - ans[E.id[j]]) % mod;
192         }
193     }
194     for(R i = 1; i <= m; i++) printf("%d
", ans[i]);
195 }
196 
197 int main()
198 {
199 //    freopen("in.in", "r", stdin);
200     fread(READ, 1, 5000000, stdin);
201     pre();
202     dfs1(1);
203     dfs2(1, 1);
204     build(1, 1, n);
205     work();
206 //    fclose(stdin);
207     return 0;
208 }
View Code

 

    

原文地址:https://www.cnblogs.com/ww3113306/p/9614645.html