zoukankan      html  css  js  c++  java
  • P1505 [国家集训队]旅游

    P1505 [国家集训队]旅游

    题目背景

    ​ Ray 乐忠于旅游,这次他来到了 T 城。T 城是一个水上城市,一共有 n个景点,有些景点之间会用一座桥连接。为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有一条路径。换句话说, T 城中只有 n−1 座桥。

    ​ Ray 发现,有些桥上可以看到美丽的景色,让人心情愉悦,但有些桥狭窄泥泞,令人烦躁。于是,他给每座桥定义一个愉悦度 w,也就是说,Ray 经过这座桥会增加 w 的愉悦度,这或许是正的也可能是负的。有时,Ray 看待同一座桥的心情也会发生改变。

    ​ 现在,Ray 想让你帮他计算从 u 景点到 v 景点能获得的总愉悦度。有时,他还想知道某段路上最美丽的桥所提供的最大愉悦度,或是某段路上最糟糕的一座桥提供的最低愉悦度。

    题目描述

    给定一棵 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 节点之间边权最小值

    保证任意时刻所有边的权值都在 [−1000,1000] 内。

    很经典的树剖题,简直就是模板,思路很好想,但是细节超多,我交了十几次才A。

    细节1:取反的时候,原最大值变为现最小值,原最小值变为现最大值,在交换的时候要记录原值,直接交换会出错;

    int tmp = t[o].Max; t[o].Max = -t[o].Min; t[o].Min = -tmp;
    //错误写法:t[o].Max = -t[o].Min; t[o].Min = -t[o].Max;
    

    细节2:题目告诉你权值有负数,在找最大值时,初值应该赋一个极小值,不能赋0;

    int querymax(int o, int l, int r, int x, int y) {
        if(x <= l && y >= r) { return t[o].Max; }
        down(o);
        int res = -1e9; //*********
        if(x <= mid) res = max(res, querymax(ls(o), l, mid, x, y));
        if(y > mid) res = max(res, querymax(rs(o), mid + 1, r, x, y));
        return res;
    }
    

    细节3:题目给的是边权,我们把边权赋到子节点去做,我们发现根节点是没有值的,自然查询最值时不能算上;

    int querypathmax(int x, int y) {
    	int res = -1e9;
    	while(top[x] != top[y]) {
    		if(dep[top[x]] < dep[top[y]]) swap(x, y);
    		int fx = top[x]; res = max(res, cj.querymax(dfn[fx], dfn[x]));
    		x = fa[fx];
    	}
    	if(dep[y] > dep[x]) swap(x, y);
    	res = max(res, cj.querymax(dfn[y] + 1, dfn[x])); //这里+1就是为了不算根节点
    	return res;
    }
    //错误写法:res = max(res, cj.querymax(dfn[y], dfn[x]));
    

    细节4:很多人可能最后一个点过不去,我看题解也有几篇被最后一个点hack了,它的数据是这样的:

    3
    2 0 9  //第一条边,对应的点是2
    1 0 8  //第二条边,对应的点是1
    8
    SUM 1 0
    C 2 7
    SUM 1 2
    SUM 1 0
    MAX 1 2
    C 1 8
    MAX 1 2
    MIN 1 0
    

    我们在执行操作"C"时,如果直接用边的编号当做点的编号就会错,所以我们应该开一个数组去找一条边对应的点:

    void get_tree(int x, int Fa){
    	dep[x] = dep[fa[x] = Fa] + 1; siz[x] = 1;
    	for(int i = head[x]; i; i = e[i].nxt) {
    		int y = e[i].to; if(y == Fa) continue;
    		a[y] = e[i].val; fin[e[i].id] = y; //e[i].id是这条边的编号(正向边反向边编号相同),它对应的点是y;
    		get_tree(y, x); siz[x] += siz[y];
    		if(siz[y] > siz[hav[x]]) hav[x] = y;
    	}
    }
    if(opt == "C") {
    	cin >> x >> y; cj.changenode(dfn[fin[x]], y); 
    }
    

    AC代码:

    #include <iostream>
    #include <cstdio>
    #include <cctype>
    #define ls(o) (o << 1)
    #define rs(o) (o << 1 | 1)
    #define mid ((l + r) >> 1)
    
    using namespace std;
    
    const int N = 2e5 + 5;
    int n, m, cnt, tot;
    int a[N], A[N], fa[N], top[N], dep[N], siz[N], dfn[N], fin[N], hav[N], head[N];
    struct edge { int nxt, to, id, val; } e[N << 1];
    
    void add(int x, int y, int z, int i) {
    	e[++cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].val = z; e[cnt].id = i;
    }
    
    void get_tree(int x, int Fa){
    	dep[x] = dep[fa[x] = Fa] + 1; siz[x] = 1;
    	for(int i = head[x]; i; i = e[i].nxt) {
    		int y = e[i].to; if(y == Fa) continue;
    		a[y] = e[i].val; fin[e[i].id] = y;
    		get_tree(y, x); siz[x] += siz[y];
    		if(siz[y] > siz[hav[x]]) hav[x] = y;
    	}
    }
    
    void get_top(int x, int topic) {
    	A[dfn[x] = ++tot] = a[x]; top[x] = topic;
    	if(!hav[x]) return ; get_top(hav[x], topic); //********
    	for(int i = head[x]; i; i = e[i].nxt) {
    		int y = e[i].to; if(y == fa[x] || y == hav[x]) continue;
    		get_top(y, y);
    	}
    }
    
    struct Tree {
    	protected:
    		struct tree { long long sum; int Min, Max, tag; } t[N << 2];
    		void up(int o) {
    			t[o].sum = t[ls(o)].sum + t[rs(o)].sum;
    			t[o].Max = max(t[ls(o)].Max, t[rs(o)].Max);
    			t[o].Min = min(t[ls(o)].Min, t[rs(o)].Min);
    		}
    		void modify(int o, int k) {
    			t[o].tag *= k; t[o].sum *= k; int tmp = t[o].Max; t[o].Max = -t[o].Min; t[o].Min = -tmp; //***tmp****
    		}
    		void down(int o) {
    			if(t[o].tag == -1) modify(ls(o), -1), modify(rs(o), -1); t[o].tag = 1;
    		}
    		void build(int o, int l, int r) {
    			t[o].tag = 1;
    			if(l == r) { t[o].sum = t[o].Min = t[o].Max = A[l]; return ; }
    			build(ls(o), l, mid); build(rs(o), mid + 1, r);
    			up(o); 	
    		}
    		void changerode(int o, int l, int r, int x, int y) {
    			if(x <= l && y >= r) { modify(o, -1); return ;}
    			down(o);
    			if(x <= mid) changerode(ls(o), l, mid, x, y);
    			if(y > mid) changerode(rs(o), mid + 1, r, x, y);
    			up(o);
    		}
    		void changenode(int o, int l, int r, int x, int k) {
    			if(l == r) { t[o].sum = t[o].Max = t[o].Min = k; return ; }
    			down(o);
    			if(x <= mid) changenode(ls(o), l, mid, x, k);
    			if(x > mid) changenode(rs(o), mid + 1, r, x, k);
    			up(o);
    		}
    		long long querysum(int o, int l, int r, int x, int y) {
    			if(x <= l && y >= r) { return t[o].sum; }
    			down(o);
    			long long res = 0;
    			if(x <= mid) res += querysum(ls(o), l, mid, x, y);
    			if(y > mid) res += querysum(rs(o), mid + 1, r, x, y);
    			return res;
    		}
    		int querymax(int o, int l, int r, int x, int y) {
    			if(x <= l && y >= r) { return t[o].Max; }
    			down(o);
    			int res = -1e9; //*********
    			if(x <= mid) res = max(res, querymax(ls(o), l, mid, x, y));
    			if(y > mid) res = max(res, querymax(rs(o), mid + 1, r, x, y));
    			return res;
    		}
    		int querymin(int o, int l, int r, int x, int y) {
    			if(x <= l && y >= r) { return t[o].Min; }
    			down(o);
    			int res = 1e9;
    			if(x <= mid) res = min(res, querymin(ls(o), l, mid, x, y));
    			if(y > mid) res = min(res, querymin(rs(o), mid + 1, r, x, y));
    			return res;
    		}
    	public:
    		void build() { build(1, 1, n); }
    		void changerode(int x, int y) { changerode(1, 1, n, x, y); }
    		void changenode(int x, int y) { changenode(1, 1, n, x, y); }
    		long long querysum(int x, int y) { return querysum(1, 1, n, x, y); }
    		int querymax(int x, int y) { return querymax(1, 1, n, x, y); }
    		int querymin(int x, int y) { return querymin(1, 1, n, x, y); }
    } cj;
    
    void changepath(int x, int y) {
    	while(top[x] != top[y]) {
    		if(dep[top[x]] < dep[top[y]]) swap(x, y);
    		int fx = top[x]; cj.changerode(dfn[fx], dfn[x]);
    		x = fa[fx];
    	}
    	if(dep[y] > dep[x]) swap(x, y);
    	cj.changerode(dfn[y] + 1, dfn[x]);
    }
    
    long long querypathsum(int x, int y) {
    	long long res = 0;
    	while(top[x] != top[y]) {
    		if(dep[top[x]] < dep[top[y]]) swap(x, y);
    		int fx = top[x]; res += cj.querysum(dfn[fx], dfn[x]);
    		x = fa[fx];
    	}
    	if(dep[y] > dep[x]) swap(x, y);
    	res += cj.querysum(dfn[y] + 1, dfn[x]);
    	return res;
    }
    
    int querypathmax(int x, int y) {
    	int res = -1e9;
    	while(top[x] != top[y]) {
    		if(dep[top[x]] < dep[top[y]]) swap(x, y);
    		int fx = top[x]; res = max(res, cj.querymax(dfn[fx], dfn[x]));
    		x = fa[fx];
    	}
    	if(dep[y] > dep[x]) swap(x, y);
    	res = max(res, cj.querymax(dfn[y] + 1, dfn[x])); 
    	return res;
    }
    
    int querypathmin(int x, int y) {
    	int res = 1e9;
    	while(top[x] != top[y]) {
    		if(dep[top[x]] < dep[top[y]]) swap(x, y);
    		int fx = top[x]; res = min(res, cj.querymin(dfn[fx], dfn[x]));
    		x = fa[fx];
    	}
    	if(dep[y] > dep[x]) swap(x, y);
    	res = min(res, cj.querymin(dfn[y] + 1, dfn[x]));
    	return res;
    }
    
    int main() {
    
    	cin >> n;
    	for(int i = 1, x, y, z;i <= n - 1; i++) {
    		cin >> x >> y >> z; x += 1; y += 1;
    		add(x, y, z, i); add(y, x, z, i);
    	}
    	get_tree(1, 0); get_top(1, 1); cj.build();
    	cin >> m;
    	for(int i = 1;i <= m; i++) {
    		string opt; int x, y; 
    		cin >> opt;
    		if(opt == "C") {
    			cin >> x >> y; cj.changenode(dfn[fin[x]], y); 
    		}
    		else if(opt == "N") {
    			cin >> x >> y; x += 1; y += 1; changepath(x, y);
    		}
    		else if(opt == "SUM") {
    			cin >> x >> y; x += 1; y += 1; printf("%lld\n", querypathsum(x, y));
    		}
    		else if(opt == "MAX") {
    			cin >> x >> y; x += 1; y += 1; printf("%d\n", querypathmax(x, y));
    		}
    		else {
    			cin >> x >> y; x += 1; y += 1; printf("%d\n", querypathmin(x, y));
    		}
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    python3笔记十五:python函数
    python3笔记十四:python可变与不可变数据类型+深浅拷贝
    python3笔记十:python数据类型-Tuple元组
    python3笔记九:python数据类型-String字符串
    python3笔记六:for语句
    python3笔记五:while语句
    python3笔记四:if语句
    python3笔记三:运算符与表达式
    springboot学习问题一:启动springboot报错端口被占用解决办法
    后向传播算法“backpropragation”详解
  • 原文地址:https://www.cnblogs.com/czhui666/p/13405893.html
Copyright © 2011-2022 走看看