zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    在中文版VS2008中安装MVC
    【原创】最优惠的企业邮局
    【推荐】双模虚拟主机 WINDOWS经济型或 UNIX经济型
    CSS基础
    第一篇文章
    一道面试题
    IIS做web server有些中文名文件不能下载
    偶遇指间流沙
    迷失的女孩
    身边的小故事二则
  • 原文地址:https://www.cnblogs.com/genshy/p/13406493.html
Copyright © 2011-2022 走看看