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

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)\)

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()));
	}
}
上一篇:冒泡排序


下一篇:web自动化学习03——quit和close区别