[SGU550]Tree Queries Online

题意:给一棵带边权的树,按给定的顺序删掉所有边,每删一条边,输出权值$w$,并把小的那块全部乘$w$,大的那块全部加$w$,强制在线

原题是sgu的,但现在sgu好像挂了,幸运地在codeforces gym里面找到这个题(题号是K)

本来这个并不是数据结构题,按题解的说法应该是用一些奇技淫巧转化一下,但为了好玩我还是写了个伪ETT练一下手

ETT全名是Euler Tour Tree,顾名思义就是用平衡树维护欧拉遍历序

对于这棵树,欧拉遍历序就是123242151,虽然它是一个环,但通常约定在根节点处断开,方便实现

这题是存边权,所以在欧拉序列$E_{1\cdots 2n-1}$中,第$i$位存$(E_i,E_{i+1})$这条边的权值

考虑删边,假设原来的根为$r$,要把$(x,fa_x)$这条边删掉

用平衡树维护序列,分割合并一下就好(显然图中$p=fa_x$)

为了找到以$x$为根的子树的位置,我们还要存$first_i$表示节点$i$第一次在欧拉序列中出现的位置,$last_i$表示节点$i$最后一次在欧拉序列中出现的位置(为了方便实现,存的都是平衡树中的节点编号)

在删边时因为丢弃了$p$,所以要判断$first_p$是否需要更新

数值操作就直接在平衡树上打标记就好了

据说ETT可以link可以cut可以换根,但是好像很困难的样子,改天去补一下(主要就是换根,换根解决了什么都解决了)

如果有兴趣可以参考这个题解的T3

p.s.第一次看到这么奇妙的强制在线法,居然是用交互的形式

#include<stdio.h>
#include<stdlib.h>
#define inf 2147483647
#define mod 99990001
#define ll long long
struct edge1{
	int to,nex,v;
}e[400010];
struct edge2{
	int a,b,v;
}ee[200010];
struct treap{
	int l,r,fa,fix,siz,mp,bl,v,ad,mu;
	treap(){
		fix=rand();
		siz=mu=1;
		mp=inf;
	}
}t[400010];
struct pair{
	int l,r;
	pair(){l=r=0;}
};
int h[200010],dep[200010],fav[200010],fir[200010],las[200010],M;
void add(int a,int b,int c){
	M++;
	e[M].to=b;
	e[M].v=c;
	e[M].nex=h[a];
	h[a]=M;
}
int min(int a,int b){return a<b?a:b;}
void pushup(int x){
	t[x].siz=t[t[x].l].siz+t[t[x].r].siz+1;
	t[x].mp=min(t[x].bl,min(t[t[x].l].mp,t[t[x].r].mp));
}
void gao(int x,int a,int b){
	t[x].v=(b+t[x].v*(ll)a)%mod;
	t[x].ad=(b+t[x].ad*(ll)a)%mod;
	t[x].mu=(t[x].mu*(ll)a)%mod;
}
void pushdown(int x){
	if(t[x].mu!=1||t[x].ad!=0){
		if(t[x].l)gao(t[x].l,t[x].mu,t[x].ad);
		if(t[x].r)gao(t[x].r,t[x].mu,t[x].ad);
		t[x].mu=1;
		t[x].ad=0;
	}
}
pair split(int x,int k){
	pair s;
	if(x==0)return s;
	pushdown(x);
	if(k<=t[t[x].l].siz){
		s=split(t[x].l,k);
		t[x].l=s.r;
		if(s.r)t[s.r].fa=x;
		s.r=x;
	}else{
		s=split(t[x].r,k-t[t[x].l].siz-1);
		t[x].r=s.l;
		if(s.l)t[s.l].fa=x;
		s.l=x;
	}
	pushup(x);
	return s;
}
int merge(int x,int y){
	if(x==0)return y;
	if(y==0)return x;
	if(t[x].fix<t[y].fix){
		pushdown(x);
		t[x].r=merge(t[x].r,y);
		t[t[x].r].fa=x;
		pushup(x);
		return x;
	}else{
		pushdown(y);
		t[y].l=merge(x,t[y].l);
		t[t[y].l].fa=y;
		pushup(y);
		return y;
	}
}
int top(int x){
	while(t[x].fa)x=t[x].fa;
	return x;
}
int lt(int x){
	int s=t[t[x].l].siz;
	while(t[x].fa){
		if(t[t[x].fa].r==x)s+=t[t[t[x].fa].l].siz+1;
		x=t[x].fa;
	}
	return s;
}
int cut(int x){
	int rt,L,R;
	pair s,t1,t2;
	rt=top(fir[x]);
	L=lt(fir[x]);
	R=lt(las[x]);
	s=split(rt,L);
	t1=split(s.l,L-1);
	t2=split(s.r,R-L+1);
	t[t2.l].fa=0;
	if(fir[t[t1.r].bl]==t1.r){
		for(x=t2.r;t[x].l;x=t[x].l);
		fir[t[t1.r].bl]=x;
	}
	t[merge(t1.l,t2.r)].fa=0;
	return t[t1.r].v;
}
void dfs(int f,int x){
	for(int i=h[x];i;i=e[i].nex){
		if(e[i].to!=f){
			dep[e[i].to]=dep[x]+1;
			fav[e[i].to]=e[i].v;
			M++;
			if(fir[x]==0)fir[x]=M;
			t[M].v=e[i].v;
			t[M].mp=t[M].bl=x;
			dfs(x,e[i].to);
		}
	}
	las[x]=++M;
	if(fir[x]==0)fir[x]=M;
	t[M].v=fav[x];
	t[M].mp=t[M].bl=x;
}
int main(){
	t[0].siz=0;
	int n,i,a,b,c;
	scanf("%d",&n);
	for(i=1;i<n;i++){
		scanf("%d%d%d",&ee[i].a,&ee[i].b,&ee[i].v);
		add(ee[i].a,ee[i].b,ee[i].v);
		add(ee[i].b,ee[i].a,ee[i].v);
	}
	M=0;
	dfs(0,1);
	for(i=1;i<M;i++)merge(top(i),i+1);
	for(i=1;i<n;i++){
		scanf("%d",&c);
		a=ee[c].a;
		b=ee[c].b;
		c=cut(dep[a]>dep[b]?a:b);
		printf("%d\n",c);
		fflush(stdout);
		a=top(fir[a]);
		b=top(fir[b]);
		if(t[a].siz<t[b].siz||(t[a].siz==t[b].siz&&t[a].mp<t[b].mp)){
			gao(a,c,0);
			gao(b,1,c);
		}else{
			gao(a,1,c);
			gao(b,c,0);
		}
	}
}

原文地址:https://www.cnblogs.com/jefflyy/p/8328235.html

时间: 2024-10-18 17:46:08

[SGU550]Tree Queries Online的相关文章

AC日记——825G - Tree Queries

825G - Tree Queries 思路: 神题,路径拆成半链: 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 1000005 #define INF 0x3f3f3f3f int n,m,val[maxn],head[maxn],E[maxn<<1],V[m

Educational Codeforces Round 25 G. Tree Queries

题目链接:Educational Codeforces Round 25 G. Tree Queries 题意: 给你一棵树,一开始所有的点全是黑色,有两种操作. 1 x 将x这个点变为黑色,保证第一个操作是这个. 2 x 询问x到任意黑色的点的简单路径上的最小节点编号. 题解: 首先将一个变为黑色的点当成树根,然后dfs一下,预处理出所有点的答案. 然后开一个变量记录一下当前变黑的点的答案cur=min(cur,dp[x]). 每次询问的时候答案就是min(cur,dp[x]). 如果觉得很神

CF1328E Tree Queries

CF1328E Tree Queries 应该还是比较妙的 题意 给你一个树,然后多次询问 每次询问给出一堆节点,问你是否能找到一个从根出发的链,是的对于给出的每个节点,都能找出链上的点,是的他们的距离小于等于\(1\) \(n\leq 2\cdot 10^5,m\leq 2\cdot 10^5,\sum k\leq 2\cdot 10^5\) 其中\(m\)是询问次数,\(k\)是每次给出的点数 首先,一个点要想符合题目的条件,无非有两种情况 一种是就在链上,这个好说 另一种是距离链上的点距离

Codeforces Round #629 (Div. 3) E. Tree Queries(lca题)

https://codeforces.com/contest/1328/problem/E E. Tree Queries You are given a rooted tree consisting of nn vertices numbered from 11 to nn. The root of the tree is a vertex number 11. A tree is a connected undirected graph with n−1n−1 edges. You are

CF-1328 E. Tree Queries

E. Tree Queries 题目链接 题意 给定一个树,每次询问一组点,问是否存在一条从根到某点的路径,使得该组点到该路径的最短距离不超过1 分析 从根到达某点的路径,如果覆盖到了某个点,那么一定会覆盖它的父亲(根除外),所以对组内的点替换成他们的父亲,问题转换为是否存在一条从根出发的路径覆盖所有的点.做法是将这些点按照深度从小到大排序,然后深度小的必须为深度大的的祖先 相邻两点求LCA即可,由于题目特殊性,前面的点和后面的点必须和根在一条直直的路径上,所以可以用欧拉序直接来判断是否可行 另

CodeForces 825G&quot;Tree Queries&quot;(建树选根)

传送门 •参考资料 [1]:CodeForces 825G Educational Round #25 G :建树选根大法+O1大法+iostream解绑了还是慢 •题意 给定一颗包含 n 个节点的树,开始树的所有节点都是白色的: 给出 q 次询问,询问分为1.2两种: 将节点 x 涂成黑色. 询问节点 x 到所有的黑点节点的简单路径中的标号最小的那个点(包括起点和黑点) 题目保证第一次询问是 1 类型的. •题解 如果我们随便选取某节点作为根节点,那么询问的时候,我们要找到节点 x 到所有黑色

CF825G Tree Queries

[题意] 一棵树有 n个节点,初始均为白色,有两种操作: 1. 1 x 代表把结点 x 设置为黑色 2. 2 x 代表查询 x 到树上任意一个黑色结点的简单路径上的编号最小的结点的编号 输入 t 和 z ,其中 t 表示操作类型, x=(last+z)mod n+1,last代表上一次询问答案,初始为 0 ,保证第一个操作为 1 [数据范围] n,q<=1e6. [题解] 发现第一个操作保证是1,则可以以第一个染黑的点为根,则每个点到它路径上最小值dis[i]可以预处理出. 发现一个点到所有黑点

[CF1328E] Tree Queries - LCA

给定一棵有根树,每次询问给定一个点集,问是否存在根到某点的链,使得点集中所有点到链的距离不大于 \(1\). Solution 将每次询问的结点按深度排序好,相邻的两个结点 \(p,q\) 一定满足 \(d[p]-d[lca] \le 1 \or d[q]-d[lca] \le 1\),其中 \(lca=lca(p,q)\) 必要性显然,充分性考虑让毛毛虫的茎一直往差值大的那边走即可 #include <bits/stdc++.h> using namespace std; #define i

Codeforces Round #629 (Div. 3) E. Tree Queries(LCA)

https://codeforces.com/contest/1328/problem/E 题目所描述的是一棵树,题中已明示1为root结点. 题目可以转化为,是否存在一条路径,满足集合中的k个点到路径的距离小于等于1? 思路: 1.首先倍增离线预处理出结点深度,便于后续在线询问LCA 2.对于每次的询问,依次扫描k个点.对于集合中的u和v两点,每次我们求出u和v的LCA,计算u和v到LCA的距离,如果u和v到LCA的距离同时大于1,那么说明无法找到一条路径,使得u和v到该路径链的距离小于等于1