DTOJ#5039. Tree

给出一棵有 N N N 个点的树,每个点的编号为 1 ∼ N 1\sim N 1N,其中 1 1 1 号点为这棵树的根。每条边有一个权值,初始时第 i i i 条边的权值为 w i w_i wi。两个点之间的距离为以这两个点为端点的简单路径上的所有边的权值和。

现在对这棵树有 M M M 次操作,每次操作的格式如下:

1 i k:将第 i i i 条边的权值修改为 k k k

2 x y:询问以 x x x 为根的子树中一点 u u u 与以 y y y 为根的子树中一点 v v v 间的最大距离;

3 x a b:询问以 x x x 为根的子树中一点 u u u 与以 a a a, b b b 为端点的简单路径上一点 v v v 间的最大距离。

1 1 1 行包含两个正整数 N , M N,M N,M,分别表示树的结点数和操作次数;

2 2 2 行至第 N N N 行,第 i + 1 i+1 i+1 行包含三个整数 u i , v i , w i u_i,v_i,w_i ui,vi,wi,表示第 i i i 条边连接节点 u i u_i ui 与节点 v i v_i vi,初始时权值为 w i w_i wi

N + 1 N+1 N+1 行至第 N + M N+M N+M 行,每行表示一次操作,格式见题目描述。

对于每次询问操作,输出一个整数表示答案。

样例输入
5 5
1 2 1
1 3 2
2 4 3
2 5 4
2 1 2
3 2 4 3
1 1 2
2 4 3
3 2 5 3
样例输出
7
7
7
8
测试点编号 N , M ≤ N,M \leq N,M w i ≤ w_i \leq wi o p t = opt= opt=特殊性质
1 1 1 500 500 500 1000 1000 1000 2 , 3 2,3 2,3 无 无
2 2 2 500 500 500 1000 1000 1000 1 , 2 , 3 1,2,3 1,2,3 无 无
3 3 3 2000 2000 2000 1000 1000 1000 1 , 2 1,2 1,2 无 无
4 4 4 2000 2000 2000 1000 1000 1000 1 , 3 1,3 1,3 C C C
5 5 5 2000 2000 2000 1 0 9 10^9 109 2 , 3 2,3 2,3 A A A
6 6 6 2000 2000 2000 1 0 9 10^9 109 1 , 2 , 3 1,2,3 1,2,3 无 无
7 7 7 5 × 1 0 4 5 \times 10^4 5×104 1000 1000 1000 2 2 2 B B B
8 8 8 5 × 1 0 4 5 \times 10^4 5×104 1000 1000 1000 3 3 3 A A A
9 9 9 5 × 1 0 4 5 \times 10^4 5×104 1 0 9 10^9 109 1 , 2 , 3 1,2,3 1,2,3 A A A
10 10 10 5 × 1 0 4 5 \times 10^4 5×104 1 0 9 10^9 109 1 , 2 , 3 1,2,3 1,2,3 B , C B,C B,C
11 ∼ 12 11\sim 12 1112 5 × 1 0 4 5 \times 10^4 5×104 1 0 9 10^9 109 1 , 2 , 3 1,2,3 1,2,3 无 无
13 13 13 1 0 5 10^5 105 1 0 9 10^9 109 1 , 2 1,2 1,2 B B B
14 14 14 1 0 5 10^5 105 1 0 9 10^9 109 1 , 3 1,3 1,3 C C C
15 15 15 1 0 5 10^5 105 1 0 9 10^9 109 2 , 3 2,3 2,3 无 无
16 16 16 1 0 5 10^5 105 1 0 9 10^9 109 1 , 2 , 3 1,2,3 1,2,3 A A A
17 ∼ 20 17\sim 20 1720 1 0 5 10^5 105 1 0 9 10^9 109 1 , 2 , 3 1,2,3 1,2,3 无 无

对于所有测试点, 1 ≤ N , M ≤ 100000 1 \leq N,M \leq 100000 1N,M100000, 1 ≤ w i , k ≤ 1 0 9 1 \leq w_i,k \leq 10^9 1wi,k109

说明

o p t opt opt 一栏表示该测试点会出现的询问种类,与输入中给出的每次询问的第一个数字相对应。

特殊性质一栏中,

A A A 表示该测试点给出的树是一条链;

B B B 表示对于所有 2 2 2操作,都有 x = y x=y x=y;

C C C 表示对于所有 3 3 3操作,都有 a = b a=b a=b

题解:
首先,发现若求出树上前缀和,可以把路径转化成 d [ x ] + d [ y ] − 2 × d [ l c a ] d[x]+d[y]-2\times d[lca] d[x]+d[y]2×d[lca]
这时,考虑把树转化成区间,但又得支持链的操作,所以用 E T T ETT ETT
假设已经建出 E T T ETT ETT 序,我们发现对于 x x x y y y 的路径,可以转化成 d [ x ] + d [ y ] − 2 × d [ z ] d[x]+d[y]-2\times d[z] d[x]+d[y]2×d[z] z z z 是区间 [ x , y ] [x,y] [x,y] d e p dep dep 最小的点,这启发我们用线段树维护。
对于 x ( x ≤ l ) x(x \leq l) x(xl) [ l , r ] [l,r] [l,r] 的询问,发现 y y y 属于 [ l , r ] [l,r] [l,r] z z z 可能在 [ x , l ] [x,l] [x,l] 中也可能在 [ l , y ] [l,y] [l,y]中,我们只需要用线段树维护区间 d e p dep dep 最小的点, d [ y ] − d [ z ] d[y]-d[z] d[y]d[z] 最大的 y y y,以及最终的答案。
对于其他情况同理。

#include<bits/stdc++.h>
#define N 100005
#define A p<<1
#define B p<<1|1
typedef long long ll;
using namespace std;
const ll inf=1e15;
inline int read(){
	int x=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	return x*f;
} 
int tot=1,head[N],ver[N<<1],nex[N<<1],dfs_num,st[N],ed[N],q[N<<1];
ll edge[N<<1];
inline void add(int x,int y,int z){
	nex[++tot]=head[x];head[x]=tot;ver[tot]=y;edge[tot]=z;
}
int dep[N];
ll d[N];
void dfs(int x,int las){
	st[x]=++dfs_num;q[dfs_num]=x;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==las)continue;
		dep[y]=dep[x]+1;
		d[y]=d[x]+edge[i];
		dfs(y,x);
		q[++dfs_num]=x;
	}
	ed[x]=dfs_num;
}
struct seg{
	int l,r;
	ll maxn[4],lazy,minn[2];
	inline void init(){
		memset(maxn,0,sizeof(maxn));lazy=0;
		minn[0]=minn[1]=inf;
	}
}t[N<<3];/*
inline void up(int p){
	t[p].maxn[0]=max(t[A].maxn[0],t[B].maxn[0]);
	t[p].maxn[1]=max(t[B].maxn[1],t[B].maxn[0]-2*t[A].minn[1]);
	t[p].maxn[2]=max(t[A].maxn[2],t[A].maxn[0]-2*t[B].minn[1]);
	t[p].maxn[3]=max(t[A].maxn[0]+t[B].maxn[1],t[A].maxn[2]+t[B].maxn[0]);
	t[p].minn[0]=min(t[A].minn[0],t[B].minn[0]);
	if(t[p].minn[0]==t[A].minn[0])t[p].minn[1]=t[A].minn[1];
	if(t[p].minn[0]==t[B].minn[0])t[p].minn[1]=t[B].minn[1];
}*/
inline seg up(seg a,seg b){
	seg x;x.l=a.l,x.r=b.r,x.lazy=0;
	x.maxn[0]=max(a.maxn[0],b.maxn[0]);
	x.maxn[1]=max(b.maxn[1],b.maxn[0]-2*a.minn[1]);x.maxn[1]=max(x.maxn[1],a.maxn[1]);
	x.maxn[2]=max(a.maxn[2],a.maxn[0]-2*b.minn[1]);x.maxn[2]=max(x.maxn[2],b.maxn[2]);
	x.maxn[3]=max(a.maxn[0]+b.maxn[1],a.maxn[2]+b.maxn[0]);x.maxn[3]=max(x.maxn[3],max(a.maxn[3],b.maxn[3]));
	x.minn[0]=min(a.minn[0],b.minn[0]);
	if(x.minn[0]==a.minn[0])x.minn[1]=a.minn[1];
	if(x.minn[0]==b.minn[0])x.minn[1]=b.minn[1];
	return x;
}
inline void push(int p,ll z){
	t[p].maxn[0]+=z;t[p].maxn[1]-=z;t[p].maxn[2]-=z;t[p].minn[1]+=z;
	t[p].lazy+=z;
}
		
void build(int p,int l,int r){
	t[p].l=l,t[p].r=r;t[p].init();
	if(l==r){
		for(int i=0;i<3;++i)t[p].maxn[i]=d[q[l]];
		for(int i=1;i<=2;++i)t[p].maxn[i]-=2*d[q[l]];
		t[p].minn[0]=dep[q[l]],t[p].minn[1]=d[q[l]];
		t[p].maxn[3]=0;
		return ;
	}
	int mid=(l+r)>>1;
	build(A,l,mid);build(B,mid+1,r);
	t[p]=up(t[A],t[B]);
	//cout<<l<<" "<<r<<" "<<t[p].maxn[0]<<endl;
}
inline void wo(int p){
	if(t[p].lazy!=0){
		push(A,t[p].lazy);push(B,t[p].lazy);t[p].lazy=0;
	}
}
void change(int p,int l,int r,ll val){
	if(t[p].l>=l&&t[p].r<=r){
		push(p,val);
		return ;
	}
	wo(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid)change(A,l,r,val);
	if(r>mid)change(B,l,r,val);
	t[p]=up(t[A],t[B]);
}
seg ask(int p,int l,int r){
	if(t[p].l>=l&&t[p].r<=r){
		return t[p];
	}
	wo(p);
	int mid=(t[p].l+t[p].r)>>1;
//	cout<<t[p].l<<" "<<t[p].r<<" "<<l<<" "<<r<<endl;
	if(r<=mid)return ask(A,l,r);
	if(l>mid)return ask(B,l,r);
	return up(ask(A,l,r),ask(B,l,r));
}
inline ll work(int xl,int xr,int yl,int yr){
	if(xl>xr||yl>yr)return 0;
	//cout<<xl<<" "<<xr<<" "<<yl<<" "<<yr<<endl;
	if(xl==yl&&xr==yr){
		//cout<<ask(1,xl,xr).maxn[3]<<endl;
		return ask(1,xl,xr).maxn[3];
	}
	ll ans=0;
	seg le=ask(1,xl,xr),re=ask(1,yl,yr);
	ans=max(le.maxn[2]+re.maxn[0],le.maxn[0]+re.maxn[1]);
	//cout<<le.maxn[2]<<" "<<re.maxn[0]<<endl;
	if(xr<yl-1){
		ans=max(ans,le.maxn[0]+re.maxn[0]-2*ask(1,xr+1,yl-1).minn[1]);
	}
	//cout<<ans<<endl;
	return ans;
}
inline ll quer(int xl,int xr,int yl,int yr){
	if(xl>yl)swap(xl,yl),swap(xr,yr);
	ll ans=0;
	if(xr<yl-1){
		ans=work(xl,xr,yl,yr);
	}else{
		if(xl==yl&&xr<yr)swap(xl,yl),swap(xr,yr);
		ans=max(work(xl,yl-1,yl,yr),work(yl,yr,yl,yr));
		ans=max(ans,work(yl,yr,yr+1,xr));
	}
	return ans;
}
int main(){
//	freopen("data.in","r",stdin);
//	freopen("tree.out","w",stdout);
	int n=read(),m=read();
	for(int i=1;i<n;++i){
		int x=read(),y=read(),z=read();
		add(x,y,z);add(y,x,z);
	}
	dep[1]=1;
	dfs(1,0);
	//for(int j=1;j<=n;++j)cout<<d[j]<<" ";cout<<endl;
	build(1,1,dfs_num);
/*	for(int i=1;i<=dfs_num;++i){
		cout<<q[i]<<" ";
	}*/
//	cout<<endl;
//	cout<<endl;
	while(m--){
		int op=read();
		if(op==1){
			int i=read()*2;ll k=read();
			int x=ver[i],y=ver[i^1];
			if(dep[x]>dep[y])swap(x,y);
			//cout<<st[y]<<" "<<ed[y]<<endl;
			change(1,st[y],ed[y],k-edge[i]);
			edge[i]=edge[i^1]=k;
		}
		if(op==2){
			int x=read(),y=read();
			ll ans=quer(st[x],ed[x],st[y],ed[y]);
			printf("%lld\n",ans);
		}
		if(op==3){
			int x=read(),a=read(),b=read();
			ll ans=max(quer(st[x],ed[x],st[a],st[a]),quer(st[x],ed[x],st[b],st[b]));
			
			//node l=quer(st[x],ed[x],st[a],st[a]),r=quer(st[x],ed[x],st[b],st[b]);
			//cout<<r.maxn[0]<<" "<<r.maxn[
			printf("%lld\n",ans);
		}
	}
	return 0;
}/*
5 4
1 2 70
1 3 53
1 4 21
3 5 2

1 1 41
1 4 51
1 1 41
3 2 1 2



*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段代码实现了在 PyQt5 中使用 QTreeView 和 QListView 显示文件系统的功能。具体解释如下: 1. 创建一个 QDirModel 类的实例,并将其赋值给 self.model。QDirModel 是一个用于显示文件系统结构的模型,它可以让我们在 QTreeView 或 QListView 中显示文件和文件夹的层次结构。 2. 调用 setReadOnly 方法,将 self.model 设置为只读。这样用户就不能通过界面来修改文件系统。 3. 创建一个 QItemSelectionModel 类的实例,并将其赋值给 selModel。QItemSelectionModel 是一个用于跟踪 QTreeView 或 QListView 中项目选择状态的模型。 4. 创建一个 QTreeView 类的实例,并将其赋值给 self.tree。QTreeView 是一个用于显示形结构的控件,可以将 self.model 中的层次结构显示为形结构。 5. 创建一个 QListView 类的实例,并将其赋值给 self.list。QListView 是一个用于显示列表结构的控件,可以将 self.model 中的层次结构显示为列表结构。 6. 将 self.model 分别设置为 self.tree 和 self.list 的模型。这样,当我们在 QTreeView 或 QListView 中选择一个项目时,对应的项目也会在另一个控件中被选中。 7. 调用 hideColumn 方法,将 self.tree 中的第 1、2、3 列隐藏起来。这些列分别是文件大小、文件类型和修改日期,我们可以据需要选择显示或隐藏它们。 8. 将 selModel 设置为 self.tree 的选择模型。这样,当我们在 QTreeView 中选择一个项目时,selModel 会跟踪选中状态。 9. 将 self.tree 的选择模型设置为 self.list 的选择模型。这样,当我们在 QTreeView 或 QListView 中选择一个项目时,另一个控件也会同步选中相应的项目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值