P1505 [国家集训队]旅游

题目描述

洛谷
给定一棵 \(n\) 个节点的树,边带权,编号 \(0∼n−1\),需要支持五种操作:

  • C i w 将输入的第 \(i\) 条边权值改为 \(w\)
  • N u v 将 \(u,v\) 节点之间的边权都变为相反数
  • SUM u v 询问 \(u,v\) 节点之间边权和
  • MAX u v 询问 \(u,v\) 节点之间边权最大值
  • MIN u v 询问 \(u,v\) 节点之间边权最小值

数据范围: 对于 100% 的数据,\(1≤n,m≤2×10^5\)

solution

一道很裸但是很毒瘤的树剖题,下放标记着实令人恶心(我在这里疯狂WA) 不说了,讲正解。

首先,抛开取反操作,剩下的操作都很正常,区间和,区间最大值,区间最小值,单点修改。 我们直接上线段树套树剖的板子就可以。

接下来我们考虑取反操作,对于线段树上维护的区间和,区间最大值,区间最小值。

取反时,区间和直接变为相反数,区间最大值和最小值交换一下,在乘以-1就可以了。

代码

    void cover(int o)
    {
    	tag(o) *= -1;
    	sum(o) = -sum(o);
    	swap(minn(o),maxn(o));
    	minn(o) = -minn(o);
    	maxn(o) = -maxn(o);
    }

我们还要考虑打标记的问题,1表示没有取反,-1表示取反。

下放时子节点的标记要乘以-1,而不是变为-1

代码

    void cover(int o)
    {
    	tag(o) *= -1;
    	sum(o) = -sum(o);
    	swap(minn(o),maxn(o));
    	minn(o) = -minn(o);
    	maxn(o) = -maxn(o);
    }
    void down(int o)
    {
    	if(tag(o) == -1)//要取反
    	{
           cover(o<<1); cover(o<<1|1);
           tag(o) = 1;
    	}
    } 

几个要注意的问题

1.开始时所有的标记全为1,而不是0

2.寻找第x条边时,应判断那个点是孩子。

3.下放标记时,孩子节点的标记要乘以-1,而不是变为-1

4.取最大值时,答案一开始要设成一个负数,不然你就会炸

总代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 2e5+10;
string opt;
int n,t,x,y,val,tot,num,u,v;
int size[N],dep[N],son[N],fa[N],top[N],head[N],a[N],w[N],from[N],to[N],dfn[N];
inline int read()
{
	int s = 0, w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
	return s * w;
}
struct node{int to,net,w;}e[N<<1];
void add_(int x,int y,int w)
{
	e[++tot].w = w;
	e[tot].to = y;
	e[tot].net = head[x];
	head[x] = tot;
}
void get_tree(int x)
{
	dep[x] = dep[fa[x]] + 1; size[x] = 1;
	for(int i = head[x]; i; i = e[i].net)
	{
		int to = e[i].to;
		if(to == fa[x]) continue;
		fa[to] = x; a[to] = e[i].w;
		get_tree(to);
		size[x] += size[to];
		if(size[to] > size[son[x]]) son[x] = to;
	}
}
void dfs(int x,int topp)
{
	top[x] = topp; dfn[x] = ++num; w[dfn[x]] = a[x];
	if(son[x]) dfs(son[x],topp);
	for(int i = head[x]; i; i = e[i].net)
	{
		int to = e[i].to;
		if(to == fa[x] || to == son[x]) continue;
		dfs(to,to);
	}
}
struct Tree
{
    struct node{
    	int lc,rc;
    	int tag,sum;
    	int maxn,minn;
    }tr[N<<2];
    #define l(o) tr[o].lc
    #define r(o) tr[o].rc
    #define tag(o) tr[o].tag
    #define sum(o) tr[o].sum
    #define maxn(o) tr[o].maxn
    #define minn(o) tr[o].minn
    void up(int o)
    {
    	sum(o) = sum(o<<1) + sum(o<<1|1);
    	maxn(o) = max(maxn(o<<1),maxn(o<<1|1));
    	minn(o) = min(minn(o<<1),minn(o<<1|1));
    }
    void cover(int o)
    {
    	tag(o) *= -1;//一定要是乘以-1
    	sum(o) = -sum(o);
    	swap(minn(o),maxn(o));
    	minn(o) = -minn(o);
    	maxn(o) = -maxn(o);
    }
    void down(int o)//下放
    {
    	if(tag(o) == -1)
    	{
           cover(o<<1); cover(o<<1|1);
           tag(o) = 1;
    	}
    } 
    void build(int o,int L,int R)
    {
        l(o) = L, r(o) = R; tag(o) = 1;//标记初始化为1
        if(L == R)
        {
        	sum(o) = minn(o) = maxn(o) = w[L];return ;
        }
        int mid = (L + R)>>1;
        build(o<<1,L,mid);
        build(o<<1|1,mid+1,R);
        up(o);
    }
    void chenge(int o,int x,int val)//单点修改
    {
        if(l(o) == r(o))
        {
        	sum(o) = minn(o) = maxn(o) = val; return;
        }
        down(o);
        int mid = (l(o) + r(o))>>1;
        if(x <= mid) chenge(o<<1,x,val);
        if(x > mid) chenge(o<<1|1,x,val);
        up(o);
    }
    void fan(int o,int L,int R)//取反操作
    {
        if(L <= l(o) && R >= r(o))
        {
        	cover(o); return;
        }
        down(o);
        int mid = (l(o) + r(o))>>1;
        if(L <= mid) fan(o<<1,L,R);
        if(R > mid) fan(o<<1|1,L,R);
        up(o);
    }
    int ask_sum(int o,int L,int R)//区间和
    {
    	int ans = 0;
    	if(L <= l(o) && R >= r(o)) return sum(o);
    	down(o);
    	int mid = (l(o) + r(o))>>1;
    	if(L <= mid) ans += ask_sum(o<<1,L,R);
    	if(R > mid) ans += ask_sum(o<<1|1,L,R);
    	return ans;
    }
    int ask_max(int o,int L,int R)//最大值
    {
    	int ans = -1010;
    	if(L <= l(o) && R >= r(o)) return maxn(o);
    	down(o);
    	int mid = (l(o) + r(o))>>1;
    	if(L <= mid) ans = max(ans,ask_max(o<<1,L,R));
    	if(R > mid) ans = max(ans,ask_max(o<<1|1,L,R));
    	return ans; 
    }
    int ask_min(int o,int L,int R)//维护区间最小值
    {
    	int ans = 1010;
    	if(L <= l(o) && R >= r(o)) return minn(o);
    	down(o);
    	int mid = (l(o) + r(o))>>1;
    	if(L <= mid) ans = min(ans,ask_min(o<<1,L,R));
    	if(R > mid) ans = min(ans,ask_min(o<<1|1,L,R));
    	return ans;
    }
}tree;
void qufan(int x,int y)
{
	while(top[x] != top[y])
	{
		if(dep[top[x]] < dep[top[y]]) swap(x,y);
		tree.fan(1,dfn[top[x]],dfn[x]);
		x = fa[top[x]];
	}
	if(dep[x] > dep[y]) swap(x,y);
	tree.fan(1,dfn[x]+1,dfn[y]);
}
int query_sum(int x,int y)
{
	int ans = 0;
	while(top[x] != top[y])
	{
		if(dep[top[x]] < dep[top[y]]) swap(x,y);
		ans += tree.ask_sum(1,dfn[top[x]],dfn[x]);
		x = fa[top[x]];
	}
	if(dep[x] > dep[y]) swap(x,y);
	ans += tree.ask_sum(1,dfn[x]+1,dfn[y]);
	return ans;
}
int query_max(int x,int y)
{
	int ans = -1010;
	while(top[x] != top[y])
	{
		if(dep[top[x]] < dep[top[y]]) swap(x,y);
		ans = max(ans,tree.ask_max(1,dfn[top[x]],dfn[x]));
		x = fa[x];
	}
	if(dep[x] > dep[y]) swap(x,y);
	ans = max(ans,tree.ask_max(1,dfn[x]+1,dfn[y]));
	return ans;
}
int query_min(int x,int y)//跳链修改
{
	int ans = 1010;
	while(top[x] != top[y])
	{
		if(dep[top[x]] < dep[top[y]]) swap(x,y);
		ans = min(ans,tree.ask_min(1,dfn[top[x]],dfn[x]));
		x = fa[x];
	}
	if(dep[x] > dep[y]) swap(x,y);
	ans = min(ans,tree.ask_min(1,dfn[x]+1,dfn[y]));
	return ans;
}
int main()
{
	n = read();
	for(int i = 1; i <= n-1; i++)
	{
		u = read() + 1; v = read() + 1; val = read();
		add_(u,v,val); add_(v,u,val);
		from[i] = u; to[i] = v;//标记每条边的起点和终点
	}
	get_tree(1); dfs(1,1); tree.build(1,1,n); //初始化
//	for(int i = 1; i <= n; i++) cout<<tree.ask_sum(1,i,i)<<endl;
	t = read();
	while(t--)
	{
		cin>>opt;
		if(opt == "C")
		{
			x = read(); val = read();
			int xx = from[x];
			int yy = to[x];
                        if(fa[xx] == yy) tree.chenge(1,dfn[xx],val);//判断谁是孩子
                        else tree.chenge(1,dfn[yy],val);
		}
		if(opt == "N")
		{
			x = read(); y = read();
			qufan(x+1,y+1);//从1开始标号,所以要+1
		}
                if(opt == "SUM")
                {
        	       x = read(); y = read();
        	       printf("%d\n",query_sum(x+1,y+1));
                }
		if(opt == "MAX")
		{
			x = read(); y = read();
			printf("%d\n",query_max(x+1,y+1));
		}
		if(opt == "MIN")
		{
			x = read(); y = read();
			printf("%d\n",query_min(x+1,y+1));
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/genshy/p/13406493.html