#55. 【WC2014】紫荆花之恋

强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。

仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点,每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 ii 都有一个感受能力值 riri,小精灵 i,ji,j 成为朋友当且仅当在树上 ii 和 jj 的距离 dist(i,j)≤ri+rjdist(i,j)≤ri+rj,其中 dist(i,j)dist(i,j) 表示在这个树上从 ii 到 jj 的唯一路径上所有边的边权和。

强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。

我们假定这个树一开始为空,节点按照加入的顺序从 11 开始编号。由于强强非常好奇,你必须在他每次出现新结点后马上给出总共的朋友对数,不能拖延哦。

输入格式

第一行包含一个整数,表示测试点编号。

第二行包含一个正整数 nn,表示总共要加入的节点数。

我们令加入节点前的总共朋友对数是 last_anslast_ans,在一开始时它的值为 00。

接下来 nn 行中第 ii 行有三个非负整数 ai,ci,riai,ci,ri,表示结点 ii 的父节点的编号为 aixor(last_ansmod109)aixor(last_ansmod109)(其中 xorxor 表示异或,modmod表示取余,数据保证这样操作后得到的结果介于 11到 i−1i−1 之间),与父结点之间的边权为 cici,节点 ii 上小精灵的感受能力值为 riri。

注意 a1=c1=0a1=c1=0,表示 11 号节点是根结点,对于 i>1i>1,父节点的编号至少为 11。

输出格式

包含 nn 行,每行输出 11 个整数,表示加入第 ii 个点之后,树上有几对朋友。

样例一

input

0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4

output

0
1
2
4
7

样例二

见样例数据下载。

限制与约定

对于所有数据,满足 1≤ci≤100001≤ci≤10000,ai≤2×109ai≤2×109,ri≤109ri≤109。

测试点编号 约定
1, 2 n≤100n≤100
3, 4 n≤1000n≤1000
5, 6, 7, 8 n≤100000n≤100000,节点 11 最多有两个子节点,其它节点最多有一个子节点
9, 10 n≤100000n≤100000,ri≤10ri≤10
11, 12 n≤100000n≤100000,这棵树是随机生成的
13, 14, 15 n≤70000n≤70000
16, 17, 18, 19, 20 n≤100000n≤100000

此题 hack 时忽略输入数据中给定的测试点编号对测试点的限制。

祝大家一遍 AC,求不虐萌萌哒测评机!

时间限制:12s12s

空间限制:512MB

  1 #include <set>
  2 #include <queue>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <iostream>
  6 #include <algorithm>
  7 #define M 100100
  8 #define ALPHA 0.88
  9 using namespace std;
 10
 11 int n;
 12 long long last_ans;
 13 int r[M];
 14
 15 namespace Graph{  //我们把这棵树除了构建点分树之外再构建一棵根为1的树,用来查询两点之间的距离
 16
 17     struct abcd{
 18         int to,f,next; //终点,边权,下一条边
 19         int ban;  //这条边能不能走(会不会走出这棵子树)
 20     }table[M<<1];  //边表
 21     int head[M]/*每个点的邻接表*/,tot=1/*边数*/;
 22
 23     int ancestor[M][17]/*表示x向上2^j的祖先 */,dpt[M]/*深度*/,dis[M]/*走到根的路径权值和*/;
 24
 25     void Add(int x,int y,int z)  //加边
 26     {
 27         table[++tot].to=y;
 28         table[tot].f=z;
 29         table[tot].next=head[x];
 30         head[x]=tot;
 31     }
 32
 33     void Build_LCA(int x)  //构建 ancestor[x][j]表示x向上2^j的祖先
 34     {
 35         int j;
 36         for(j=1;j<=16;j++)
 37             ancestor[x][j]=ancestor[ancestor[x][j-1]][j-1];
 38     }
 39
 40     int LCA(int x,int y)  //求lca
 41     {
 42         int j;
 43         if(dpt[x]<dpt[y])
 44             swap(x,y);
 45         for(j=16;~j;j--)
 46             if(dpt[ancestor[x][j]]>=dpt[y])
 47                 x=ancestor[x][j];
 48         if(x==y) return x;
 49         for(j=16;~j;j--)
 50             if(ancestor[x][j]!=ancestor[y][j])
 51                 x=ancestor[x][j],y=ancestor[y][j];
 52         return ancestor[x][0];
 53     }
 54
 55     int Distance(int x,int y)  //求两点距离
 56     {
 57         int lca=LCA(x,y);
 58         return dis[x]+dis[y]-2*dis[lca];
 59     }
 60
 61 }
 62
 63 struct Treap{
 64
 65     static queue<Treap*> bin;  //垃圾桶,用来写垃圾回收
 66
 67     Treap *ls,*rs;  //左右子树
 68     int val,key;  //键值,优先级
 69     int cnt,size;  //cnt表示这个数的个数,size表示这个点子树的大小
 70
 71     void* operator new (size_t,int _)
 72     {
 73         Treap *re;
 74         if(bin.size())
 75             re=bin.front(),bin.pop();
 76         else
 77         {
 78             static Treap *mempool,*C;
 79             if(C==mempool)
 80                 mempool=(C=new Treap[1<<16])+(1<<16);
 81             re=C++;
 82         }
 83         re->ls=re->rs=0x0;
 84         re->val=_;re->key=rand();//信仰!
 85         re->cnt=re->size=1;
 86         return re;
 87     }
 88
 89     void operator delete (void *p)
 90     {
 91         bin.push((Treap*)p);
 92     }
 93
 94     void Push_Up()  //maintain
 95     {
 96         size=cnt;
 97         if(ls) size+=ls->size;
 98         if(rs) size+=rs->size;
 99     }
100
101     friend void Zig(Treap *&x)
102     {
103         Treap *y=x->ls;
104         x->ls=y->rs;
105         y->rs=x;x=y;
106         x->rs->Push_Up();
107         x->Push_Up();
108     }
109
110     friend void Zag(Treap *&x)
111     {
112         Treap *y=x->rs;
113         x->rs=y->ls;
114         y->ls=x;x=y;
115         x->ls->Push_Up();
116         x->Push_Up();
117     }
118
119     friend void Insert(Treap *&x,int y)
120     {
121         if(!x)
122         {
123             x=new (y)Treap;
124             return ;
125         }
126         if(y==x->val)
127             x->cnt++;
128         else if(y<x->val)
129         {
130             Insert(x->ls,y);
131             if(x->ls->key>x->key)
132                 Zig(x);
133         }
134         else
135         {
136             Insert(x->rs,y);
137             if(x->rs->key>x->key)
138                 Zag(x);
139         }
140         x->Push_Up();
141     }
142
143     friend void Decomposition(Treap *&x)  //删除整棵树
144     {
145         if(!x) return ;
146         Decomposition(x->ls);
147         Decomposition(x->rs);
148         delete x;x=0x0;
149     }
150
151     friend int Query(Treap *x,int y)  //求比y小的数的个数
152     {
153         if(!x) return 0;
154         if(y<x->val)
155             return Query(x->ls,y);
156         else
157             return (x->ls?x->ls->size:0) + x->cnt + Query(x->rs,y);
158     }
159
160 };
161
162 queue<Treap*> Treap :: bin;
163
164 namespace Dynamic_TDC{
165
166     using namespace Graph;
167
168     int fa[M]/*点分树上的fa*/,v[M],T;
169
170     Treap *tree1[M]/*这个点集所有点的权值构成的treap*/,*tree2[M]/*这个点集一棵子树的权值构成的treap*/;
171
172     set<int> to[M];  //点分树上的儿子
173
174     void DFS(int x)  //删除整棵子树(这里指点分树上的子树)
175     {
176         set<int>::iterator it;
177         v[x]=T;
178         for(it=to[x].begin();it!=to[x].end();it++)  //枚举点分树上的所有儿子
179         {
180             DFS(*it);
181             Decomposition(tree2[*it]);
182         }
183         to[x].clear();
184         Decomposition(tree1[x]);
185     }
186
187     int Get_Size(int x,int from)  //求树的大小
188     {
189         int i,re=1;
190         for(i=head[x];i;i=table[i].next)  //遍历从这个点出发的每条边
191         {
192             if(v[table[i].to]!=T)
193                 continue;
194             if(table[i].ban==T)
195                 continue;
196             if(table[i].to==from)
197                 continue;  //记录上一个点,这个点不能回到上一个点
198             re+=Get_Size(table[i].to,x);
199         }
200         return re;
201     }
202
203     int Get_Centre_Of_Gravity(int x,int from,int size,int &cg)  //求重心
204     {
205         int i,re=1,flag=true;
206         for(i=head[x];i;i=table[i].next)  //遍历从这个点出发的每条边
207         {
208             if(v[table[i].to]!=T)
209                 continue;
210             if(table[i].ban==T)
211                 continue;
212             if(table[i].to==from)
213                 continue;  //记录上一个点,这个点不能回到上一个点
214             int temp=Get_Centre_Of_Gravity(table[i].to,x,size,cg);
215             if(temp<<1>size) flag=false;
216             re+=temp;
217         }
218         if(size-re<<1>size) flag=false;
219         if(flag) cg=x;
220         return re;
221     }
222
223     void DFS(int x,int from,int dpt,Treap *&p)  //把所有在这棵子树的dep[x]-r[x]插入到treap中
224     {
225         int i;
226         Insert(p,dpt-r[x]);
227         for(i=head[x];i;i=table[i].next)
228         {
229             if(v[table[i].to]!=T)
230                 continue;
231             if(table[i].ban==T)
232                 continue;
233             if(table[i].to==from)
234                 continue;
235             DFS(table[i].to,x,dpt+table[i].f,p);
236         }
237     }
238
239     int Tree_Divide_And_Conquer(int x)  //点分
240     {
241         int i,size=Get_Size(x,0);  //得到整棵树的size
242         Get_Centre_Of_Gravity(x,0,size,x);  //得到重心,在这之后x为根
243         DFS(x,0,0,tree1[x]);  //把所有在这棵子树的dep[x]-r[x]插入到treap中
244         for(i=head[x];i;i=table[i].next)
245         {
246             if(v[table[i].to]!=T)
247                 continue;
248             if(table[i].ban==T)
249                 continue;
250
251             Treap *p=0x0;
252             DFS(table[i].to,x,table[i].f,p);  //把所有在这棵子树的dep[x]-r[x]插入到treap中
253
254             table[i].ban=table[i^1].ban=T;  //这条边会通向上一个分治中心,如果走了这条边就会走到外面
255
256             int temp=Tree_Divide_And_Conquer(table[i].to);  //分治,递归各子树,temp为各子树的重心
257             tree2[temp]=p;fa[temp]=x;/*不是树上的fa,是点分树上的fa*/to[x].insert(temp);
258         }
259         return x;
260     }
261
262     void Rebuild(int x)
263     {
264         ++T;/*新建点分树上的节点*/DFS(x);  //把x的子树直接整棵删除
265         int y=fa[x];  //得到点分树上的父亲
266         Treap *p=tree2[x];  //tree2是以y为根时x这个点集的dep[i]-r[i]所构成的treap,所以不会改变,应当保留
267         tree2[x]=0x0;
268         int temp=Tree_Divide_And_Conquer(x);   //重新点分之后的节点 (重构后的这棵点分树的子树的根)
269         fa[temp]=y;if(y) to[y].insert(temp);  //此处因为不仅要插入新的子节点,还要把原来的删掉,所以要加一句to[y].erase(x);
270         tree2[temp]=p;  //保留原来的tree2
271     }
272
273     void Insert(int x)  //在点分树上插入新节点x
274     {
275         int i;
276
277         for(i=x;i;i=fa[i])  //找到点分树上x所在的所有点集
278         {
279
280             if(fa[i])  //如果有上一层的点分树
281             {
282                 int d1=Distance(x,fa[i]);
283                 last_ans+=Query(tree1[fa[i]],r[x]-d1);
284                 last_ans-=Query(tree2[i],r[x]-d1);//计算上一层的点分树会带来多少收益
285                 Insert(tree2[i],d1-r[x]);  //并在以fa[i]为根的树中i这棵子树的treap中插入关于x的信息
286             }
287
288             int d=Distance(x,i);
289             Insert(tree1[i],d-r[x]);  //在以i为根的这个点集的treap中插入关于x的信息
290
291         }
292
293         //前方野生替罪咩预警!
294
295         int to_rebuild=0;
296
297         for(i=x;fa[i];i=fa[i])
298             if( (double)tree1[i]->size/tree1[fa[i]]->size > ALPHA )  //如果一个节点(fa[i])的子树不满足替罪羊树的性质 (有一个子树太大了)
299                 to_rebuild=fa[i];  //保证找到的是点分树上深度最浅的节点 ,由于如果重建了深度最浅的节点,就等于重建了它的整棵子树,所以它子树的节点自然不需要重建
300
301         if(to_rebuild)
302             Rebuild(to_rebuild);
303     }
304
305 }
306 int main()
307 {
308     #ifndef ONLINE_JUDGE
309     freopen("3435.in","r",stdin);
310     freopen("3435.out","w",stdout);
311     #endif
312
313     srand(19980402);//我様は最強ね!にゃんははははは!~!
314     int i,x,y,z;
315     scanf("%*d%d",&n);
316     for(i=1;i<=n;i++)
317     {
318         scanf("%d%d%d",&x,&y,&z);
319         #ifdef ONLINE_JUDGE
320         x=x^(last_ans%1000000000);
321         #endif
322
323         Graph::Add(i,x,y);
324         Graph::Add(x,i,y);
325         Graph::ancestor[i][0]=x;
326         Graph::dpt[i]=Graph::dpt[x]+1;
327         Graph::dis[i]=Graph::dis[x]+y;
328         Graph::Build_LCA(i);
329         r[i]=z;  //更新原树(即以1为根,用来查询两点距离的树)
330
331         Dynamic_TDC::to[x].insert(i);
332         Dynamic_TDC::fa[i]=x;
333         Dynamic_TDC::Insert(i);  //在点分树上插入只有i这个点的点集
334
335         #ifdef ONLINE_JUDGE
336             printf("%lld\n",last_ans);
337         #else
338             printf("%I64d\n",last_ans);
339         #endif
340     }
341 }  
时间: 2024-10-29 19:10:10

#55. 【WC2014】紫荆花之恋的相关文章

UOJ#55 [WC2014]紫荆花之恋

题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这个大树实际上是一个带权树.每个时刻它会长出一个新的叶子节点,每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵.小精灵是很萌但是也很脆弱的生物,每个小精灵 \(i\) 都有一个感受能力值 \(r_i\),小精灵 \(i, j\) 成为朋友当且仅当在树上 \(i\) 和 \(j\) 的距离 \(\text{

Wc2014 紫荆花之恋

哈哈哈哈哈哈哈哈哈哈哈哈我终于过了!!!!!!!!!!!!!!! 从昨天上午就开始写了,下午回家之后调了一会儿没什么感觉,就删了重打了一遍,然后调了一晚上+今天半个上午......我*******终于过了...... 我是萌萌的传送门 我是另一个萌萌的传送门 一道极其恶心的动态树分治...... 首先点分治,限制条件就变成了di+dj<=ri+rj,移项得rj-dj>=di-ri,对重心和子树开平衡树维护di-ri即可. 查询的时候先跳点分治树更新答案,然后逐层把di-ri插入平衡树,上跳的过

bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 &amp;&amp; AC400

3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec  Memory Limit: 512 MBSubmit: 159  Solved: 40[Submit][Status][Discuss] Description 强 强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这个大树实际上是一个带权树.每个时刻它会长出一个新的叶子节点.每个节点上有一个可爱的小精灵,

【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT

[BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每个时刻它会长出一个新的叶子节点.每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵.小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离

BZOJ 3435: [Wc2014]紫荆花之恋

二次联通门 : BZOJ 3435: [Wc2014]紫荆花之恋 二次联通门 : luogu P3920 [WC2014]紫荆花之恋 /* luogu P3920 [WC2014]紫荆花之恋 怀疑人生 */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <ctime> #include <algorithm> #de

【bzoj3435】[Wc2014]紫荆花之恋 替罪点分树套SBT

题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每个时刻它会长出一个新的叶子节点.每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵.小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离 dist(i,j) ≤ Ri + R! ,其中 dist(i

BZOJ 3435 Wc2014 紫荆花之恋 动态树分治+替罪羊树+Treap

题目大意:给定一棵树,每次添加一个节点并询问当前有多少点对满足dis(i,j)<=ri+rj 强制在线 吾辈有生之年终于把这道题切了...QAQ 什么?你想做这题? 1095切了么?没?去把1095切掉再说! 3065切了么?没?去把3065切掉再说! 什么?都切了?那还不会做这题?? -- 算了还是说说做法吧... 我们抛开那些乱七八糟的,考虑朴素做法 首先式子的形式是dis(i,j)<=ri+rj,令p=lca(i,j),把式子变形可以得到dis(j,p)-rj<=ri-dis(i,

P3920 [WC2014]紫荆花之恋 [点分树,替罪羊树]

点分树搞出来,然后搞个快点的平衡树. 点分树膨胀的时候就搞重构,没了...说的这么轻巧倒是写了3h 23333 // powered by c++11 // by Isaunoya #include <bits/stdc++.h> #define rep(i, x, y) for (register int i = (x); i <= (y); ++i) #define Rep(i, x, y) for (register int i = (x); i >= (y); --i) #

数据结构(平衡树,树分治,暴力重构):WC 2014 紫荆花之恋

[题目描述] 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这棵大树实际上是一个带权树.每个时刻他会长出一个新的叶子节点.每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精 灵.小精灵是很萌但是也很脆弱的生物,每个小精灵i都有一个感受能力ri,小精灵i,j成为朋友当且仅当在树上i和j的距离 dist(i,j)<=ri+rj,其中dist(i,j)表示在这棵树上i和