Scx117
只一眼,便辽阔了时间。

题意:这是一道交互题。交互库中有一棵树。一开始只有1节点已知。需要在T次询问内使得n个节点都已知。一次询问explore(x,y),返回从x到y路径上第一个点,并将返回点标记为已知。

数据有区分。

标程:

 1 #include<cstdio>
 2 #include<time.h>
 3 #include<algorithm>
 4 #include "rts.h"
 5 using namespace std;
 6 const int N=300005;
 7 int son[N][2],fa[N],L[N],R[N],vis[N],E[2],id[N],now,nxt,it;
 8 int is_rt(int x) {return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
 9 int ran() {return (int)(rand()/RAND_MAX+0.5);}
10 void up(int x)
11 {
12     L[x]=R[x]=x;
13     if (son[x][0]) L[x]=L[son[x][0]];//leftest 
14     if (son[x][1]) R[x]=R[son[x][1]];//rightest 
15 }
16 void rot(int x)
17 {
18     int y=fa[x],z=fa[y],l=(son[y][1]==x),r=l^1;
19     if (!is_rt(y)) son[z][(son[z][1]==y)]=x;
20     fa[x]=z;fa[y]=x;fa[son[x][r]]=y;
21     son[y][l]=son[x][r];son[x][r]=y;
22     up(y);up(x);
23 }
24 void spl(int x)
25 {
26     for (int y;!is_rt(x);rot(x))
27       if (!is_rt(y=fa[x])) 
28         if (son[y][0]==x^son[fa[y]][0]==y) rot(x);else rot(y);
29 }
30 int find_rt(int x)
31 {
32     for (;!is_rt(x);x=fa[x]);
33     return x;
34 }    
35 void accs(int x) {for (int t=0;x;t=x,x=fa[x]) spl(x),son[x][1]=t,up(x);}
36 void ins(int x)
37 {
38     now=find_rt(1);
39     while (!vis[x])
40     {
41         nxt=explore(now,x);
42         if (nxt==R[son[now][0]]) now=son[now][0];//father 
43         else if (nxt==L[son[now][1]]) now=son[now][1];//son
44         else {//another splay 
45             if (vis[nxt]) now=find_rt(nxt);//already have existed
46             else vis[nxt]=1,fa[nxt]=now,now=nxt;//add a new point
47         }
48     }
49     accs(x);
50 }
51 void play(int n,int T,int dataType)
52 {
53     srand(time(NULL));
54     vis[1]=1;
55         for (int i=1;i<=n;i++) id[i]=i;
56     random_shuffle(id+2,id+n+1);
57     if (dataType==3)//chain
58     {
59         now=explore(1,id[2]);vis[now]=1;
60         E[0]=1;E[1]=now;
61         for (int i=2;i<=n;i++)
62           if (!vis[id[i]])
63           {
64                it=ran();now=explore(E[it],id[i]);//注意explore的顺序(u->v)
65                if (!vis[now])//这一边 
66                {
67                    vis[E[it]=now]=1; 
68                    while (E[it]!=id[i]) E[it]=explore(E[it],id[i]),vis[E[it]]=1;//记得更改范围指针
69              }else {//另一边
70                  it^=1;
71                  while (E[it]!=id[i]) E[it]=explore(E[it],id[i]),vis[E[it]]=1;
72              }
73           }
74         return;
75     }
76     for (int i=1;i<=n;i++) L[i]=R[i]=i;//初始化
77     for (int i=2;i<=n;i++)
78       if (!vis[id[i]]) ins(id[i]);
79 }

 

易错点:1.注意lct之前的初始化L[i]=R[i]=i。并且不用每找到一个点就splay,新加入一个点access更新保证复杂度即可。

2.access不要写错,判定继续的条件应该是x而不是!is_rt(x)。

3.链的情况要更新边界。

 

技巧:可以用random_shuffle来避免一些数据的卡。

 

题解:lct/动态点分树

数据告诉我们:树T=nlogn,链T=n+logn.

树:

1.仿照CF772E的做法,可以通过点分来插入一个点。但不同的是,这不是一棵二叉树,在前向星上插入删除的复杂度太高,因此考虑动态点分树来支持动态加点。(然而我并不会写)

2.点分的思想也就是对树二分。因此我们树链剖分后在重链上二分也可以得到一样的效果。由于有加点,用lct即可。用splay维护每一条重链,因为splay的高度的log级别的,从根开始跳splay上的节点完成二分。讨论一下返回的已知点在询问点的儿子/父亲/另一个splay上->是否已知。最后access(x)更新树的形态。O(nlogn)。

链:

维护一段连续的已知点[L,R],对于一个未知点x,如果explore(L,x)是未知点,那么往R方向扩展,反之在L方向扩展。

每个点必定被explore一次O(n)。random第一次的询问是L还是R后,更改方向的次数期望是O(log n)或是说O(ln n)的。(证明:设E(n)表示扩展为长度为n的序列需要更改几次方向:E(n)=1+1/n*sigma(E[i])(i=[1,n-1])->E(n)-E(n-1)=1/n->E(n)=sigma(1/i)≈ln n)

所以总的是O(n+log n)。

 

posted on 2018-04-05 09:42  Scx117  阅读(173)  评论(0编辑  收藏  举报