Codechef March Challenge 2021 Random Walk Queries(RWALKS) (动态点分治)

题目大意:

对于给定的无根树$T$,要求强制在线维护两种操作

1.游走$(u,d)$,以$u$为根在树上游走,从$u$开始,最多走$d$步,每次随机从儿子中选择一个点

2.查询$u$,当前$u$被遍历的期望次数

灵光一闪想到这么个憨批树上结构

对于更新$(u,d)$,考虑$u$ 跨过当前点分根 到达其他点分子树里的贡献

一个点由当前点分根到达的概率是一个定值,可以预处理出来,并在查询时计算

因此更新贡献时,可以描述为$dep\leq d$的点接受到 以$x$的概率访问当前点分根

可以简单用树状数组维护

为了剔除对于自己所在子树的非法贡献,需要额外开一些树状数组来维护

一个节点有$\log n$个点分父节点,每次需要两次树状数组查询

因此查询部分复杂度为$O(m\log ^2n)$,预处理以及空间复杂度为$O(n\log n)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
const int N=2e5+10,K=19,P=1e9+7;

int n,m,I[N];
struct Edge{
int to,nxt;
}e[N<<1];
int head[N],ecnt,deg[N];
void AddEdge(int u,int v){
e[++ecnt]=(Edge){v,head[u]};
head[u]=ecnt,deg[v]++;
}
#define erep(u) for(int i=head[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)

struct BIT{
int n;
vector <int> s;
BIT(){ };
BIT(int n):n(n){ s.resize(n+1); }
void Add(int p,int x){
for(cmin(p,n);p;p-=p&-p) s[p]+=x,Mod1(s[p]);
}
int Que(int p){
int res=0;
while(p<=n) res+=s[p],Mod1(res),p+=p&-p;
return res;
}
} T[N];
vector <BIT> G[N];
// Dep:点分树上的dep,id:节点在每层的编号, dep:节点在每层的dep,s:节点在每层由根到达的系数
int Dep[N],id[K][N],dep[K][N],s[K][N],vis[N],sz[N],fa[N],Root;

int mi,rt;
void FindRt(int n,int u,int f){
int ma=0; sz[u]=1;
erep(u) if(v!=f && !vis[v]) {
FindRt(n,v,u);
sz[u]+=sz[v],cmax(ma,sz[v]);
}
cmax(ma,n-sz[u]);
if(mi>ma) mi=ma,rt=u;
}

int D,maxd;
void dfs(int u,int f,int id){
cmax(maxd,dep[D][u]=dep[D][f]+1),::id[D][u]=id;
erep(u) if(v!=f && !vis[v]) {
s[D][v]=1ll*s[D][u]*I[deg[u]-1]%P;
dfs(v,u,id);
}
}

// 预处理点分治,开树状数组
int Divide(int n,int u){
mi=1e9,FindRt(n,u,0),u=rt;
int sonc=0;
vis[u]=s[Dep[u]=D][u]=1,id[D][u]=-1;
int t=0;
erep(u) if(!vis[v]) {
maxd=0;
s[D][v]=1,dfs(v,u,sonc);
G[u].pb(BIT(maxd));
sonc++;
cmax(t,maxd);
}
T[u]=BIT(t);
erep(u) if(!vis[v]) {
if(sz[v]>sz[u]) sz[v]=n-sz[u];
D++,fa[Divide(sz[v],v)]=u,D--;
}
return u;
}

int sum[N];
int Que(int u){
ll ans=sum[u];
for(int v=u,d=Dep[v];(d--,v=fa[v]);)
ans=(ans+ 1ll* (T[v].Que(dep[d][u])+G[v][id[d][u]].Que(dep[d][u])) *s[d][u])%P;
return (ans%P+P)%P;
}
void Upd(int u,int d){
sum[u]++,Mod1(sum[u]),T[u].Add(d,I[deg[u]]);
for(int v=fa[u],D=Dep[u]-1;v;v=fa[v],D--) {
if(d<dep[D][u]) continue;
int x=1ll*I[deg[u]]*s[D][u]%P;
sum[v]+=x,Mod1(sum[v]);
x=1ll*x*I[deg[v]-1]%P;
T[v].Add(d-dep[D][u],x),G[v][id[D][u]].Add(d-dep[D][u],P-x);
}
}

int lst;
int Get() { return (rd()+lst)%n+1; }

int main(){
I[0]=I[1]=1;
rep(i,2,N-1) I[i]=1ll*(P-P/i)*I[P%i]%P;
n=rd(),m=rd();
rep(i,2,n){
int u=rd(),v=rd();
AddEdge(u,v),AddEdge(v,u);
}
Root=Divide(n,1);
while(m--) {
int opt=rd();
if(opt==1) {
int u=Get(),d=Get();
Upd(u,d);
} else printf("%d\n",lst=Que(Get()));
}
}