首页 > 其他分享 >P4069 [SDOI2016]游戏 李超线段树 维护区间优势线段的线段树

P4069 [SDOI2016]游戏 李超线段树 维护区间优势线段的线段树

时间:2023-04-17 16:24:11浏览次数:49  
标签:std int 线段 tr 李超 SDOI2016 calc ll nw

传送门

#include <iostream>
#include <algorithm>
#include <cstring>

typedef long long ll;
typedef std::pair<double, int> PDI;

const int N = 1e5, M = 2e5 + 10;
const ll INF = 123456789123456789;
int n, m, cnt;

int h[N], e[M], ne[M], idx;
int id[N], cnt1;
int dep[N], sz[N], top[N], fa[N], son[N];
ll len[N], w[M], nw[N];

inline void add(int a, int b, ll c){
	ne[idx] = h[a], e[idx] = b, w[idx] = c, h[a] = idx ++;
}

struct Segment {
    ll k, b;
}seg[M << 1];

struct node {
    int l, r;
    ll mn;
    int id;
}tr[N << 2];

inline void ADD(ll k, ll b) {
	seg[++ cnt] = {k, b};
}

inline int cmp(ll x, ll y) {
    if(x < y)   return 1;
    else if(y > x)  return -1;
    return 0;
}

inline ll calc(int id, ll x) {
    return seg[id].k * x + seg[id].b;
}

inline void pushup(int u) {
	tr[u].mn = std::min(std::min(tr[u << 1].mn, tr[u].mn), tr[u << 1 | 1].mn);
}

inline void build_tree(int u, int l, int r) {
    tr[u] = {l, r, INF};
    if(l == r)	return ;
	
    int mid = l + r >> 1;
    build_tree(u << 1, l, mid);
    build_tree(u << 1 | 1, mid + 1, r);
}
 
inline void update(int u, int v) {
    int& k = tr[u].id;
    int mid = tr[u].l + tr[u].r >> 1;
    tr[u].mn = std::min(std::min(tr[u].mn, calc(v, nw[tr[u].l])), calc(v, nw[tr[u].r]));
    tr[u].mn = std::min(std::min(tr[u].mn, calc(k, nw[tr[u].r])), calc(k, nw[tr[u].r]));
    if(cmp(calc(v, nw[mid]), calc(k, nw[mid])) == 1)   std::swap(k, v);
    int d1 = cmp(calc(v, nw[tr[u].l]), calc(k, nw[tr[u].l])), d2 = cmp(calc(v, nw[tr[u].r]), calc(k, nw[tr[u].r]));

    if(d1 == 1)    update(u << 1, v);
    if(d2 == 1)    update(u << 1 | 1, v);
}

inline void modify(int u, int l, int r) {
    if(tr[u].l >= l && tr[u].r <= r){//定位update区间
        update(u, cnt);
        return ;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if(l <= mid)    modify(u << 1, l, r);
    if(r > mid) modify(u << 1 | 1, l, r);
    
    pushup(u);
}


inline ll query(int u, int l, int r) {
	ll res = INF;
	if(tr[u].l >= l && tr[u].r <= r)	return tr[u].mn;
    
    int mid = tr[u].l + tr[u].r >> 1;
    
	res = std::min({res, calc(tr[u].id, nw[std::max(tr[u].l, l)]), calc(tr[u].id, nw[std::min(tr[u].r, r)])});
	if (l <= mid)	res = std::min(res, query(u << 1, l, r));
	if (r > mid)	res = std::min(res, query(u << 1 | 1, l, r));
	
	return res;
}

//统计子树大小 并且 找出重儿子
inline void dfs1(int u, int father, int depth, ll cd){
    dep[u] = depth, fa[u] = father, sz[u] = 1;
    len[u] = cd;
    
    for (int i = h[u]; ~i; i = ne[i]){
        int j = e[i];
        if (j == father) continue;
        dfs1(j, u, depth + 1, cd + w[i]);
        sz[u] += sz[j];
        if (sz[son[u]] < sz[j]) son[u] = j;
    }
}

//dfs序 nw标记dfs为cnt的时候对应的树上点的权值 top标记父亲是谁
inline void dfs2(int u, int t){
    id[u] = ++ cnt1,  top[u] = t, nw[cnt1] = len[u];
    if (!son[u]) return;
    dfs2(son[u], t);
    for (int i = h[u]; ~i; i = ne[i]){
        int j = e[i];
        if (j == fa[u] || j == son[u]) continue;
        dfs2(j, j);
    }
}

inline int LCA(int u, int v) {
	while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
        if(u == fa[top[u]])	return u;
        u = fa[top[u]];
    }
    if (dep[u] < dep[v]) return u;
    return v;
}
//传进来想要爬的两个点
inline void update_path(int u, int v) { 

    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
        modify(1, id[top[u]], id[u]);
        u = fa[top[u]];
    }
    
    if (dep[u] < dep[v]) std::swap(u, v);
	modify(1, id[v], id[u]);
}

inline ll query_path(int u, int v){
	ll mn = INF;
    while (top[u] != top[v])
    {
        if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
        mn = std::min(mn, query(1, id[top[u]], id[u]));
        u = fa[top[u]];
    }
    if (dep[u] < dep[v]) std::swap(u, v);
    mn = std::min(mn, query(1, id[v], id[u]));
    
    return mn;
}

inline void Init(){
	dfs1(1, -1, 1, 0);
    dfs2(1, 1);
}

inline void solve(){
	memset(h, -1, sizeof h);
	
	std::cin >> n >> m;
	
	for(int i = 1; i < n; i ++) {
		int a, b;
		ll c;
		std::cin >> a >> b >> c;
		add(a, b, c);
		add(b, a, c);
	}
	
	Init();

    build_tree(1, 1, n);
	
	seg[0] = {0, INF};
	
	int op, u, v;
	ll a, b;
	while(m --) {
		std::cin >> op >> u >> v;
		if(op == 1){
			int lca = LCA(u, v);
			std::cin >> a >> b;
			
			ll k = -a, c = b + a * len[u];
			ADD(k, c);
			update_path(u, lca);
			
			k = a, c = a * (len[u] - len[lca] * 2) + b;
			ADD(k, c);
			update_path(lca, v);
		}else{
			std::cout << query_path(u, v) << '\n';
		}
	}
}

int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    
    int _ = 1;
    while(_ --)
    	solve();

    return 0;
}

标签:std,int,线段,tr,李超,SDOI2016,calc,ll,nw
From: https://www.cnblogs.com/qdhys/p/17310948.html

相关文章

  • 2022年江西省大学生程序设计竞赛 K.Peach Conference 线段树 懒标记清空
    传送门大致题意:  给定一个n和m,表示有区间大小为n,进行m次操作。  输入m行,每行3个数字v,l,r。如果v等于0则表示查询[l,r]内桃子的数量,如果v不为0则表示给[l,r]区间修改全部加v,如果有某个点数量+v小于0,则修改为0即可。大致思路:  这个题和势能也还是有些关系的。如果要......
  • 线段树
    一.概述线段树(SegmentTree)是一种用于处理区间查询和更新的数据结构常用于解决一维区间相关的问题,如区间最值、区间和、区间乘积等线段树的基本思想是将区间划分为一些小的子区间,并在每个子区间上维护一些信息例如该区间的最值、和、乘积等,通过将大区间不断划分为小区间,直到......
  • poj2750(线段树+复杂区间合并)
    PottedFlowerPOJ-2750思路:我们将题目简单化,假设我们要求的是序列的最大连续子段和,且可以包括所有数。我们的线段树需要维护这段区间的最大前缀和pre,最大后缀和suf,区间和sum,区间连续最大和mx。那么难点就在于如何由子节点更新父节点。我们可以知道,tr[p].sum=tr[lc].sum......
  • 删除无效的括号(广度优先搜索、字符串)、计算右侧小于当前元素的个数(树状数组、线段
    删除无效的括号(广度优先搜索、字符串)给你一个由若干括号和字母组成的字符串s,删除最小数量的无效括号,使得输入的字符串有效。返回所有可能的结果。答案可以按任意顺序返回。示例1:输入:s="()())()"输出:["(())()","()()()"]示例2:输入:s="(a)())()"输出:["(a())()","(......
  • 【230414-3】有3种不同颜色的灯泡,要在如图所示的6个点A、B、C,A',B‘,C’上各安装1个灯
    ......
  • poj2777(线段树)
    CountColorPOJ-2777思路:暴力能过,线段树维护这个区间的颜色,如果是混色则置为1,如果是单一颜色则设为这个颜色,修改就是正常的区间修改,区间查询就要变一下。还有题解是用二进制做得,可以学一下。#define_CRT_SECURE_NO_WARNINGS1#include<algorithm>#include<fstream>#inc......
  • 线段树(单点修改,区间查询)
    题目描述如题,已知一个数列,你需要进行下面两种操作:将某一个数加上 x求出某区间每一个数的和输入格式第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。接下来 ......
  • POJ 2299 Ultra-QuickSort(线段树+离散化)
    题目地址:POJ2299这题曾经用归并排序做过,线段树加上离散化也可以做。一般线段树的话会超时。这题的数字最大到10^10次方,显然太大,但是可以利用下标,下标总共只有50w。可以从数字大的开始向树上加点,然后统计下标比它小即在它左边的数的个数。因为每加一个数的时候,比该数大的数已经加完......
  • POJ 3468 A Simple Problem with Integers(线段树区间更新)
    题目地址:POJ3468打了个篮球回来果然神经有点冲动。。无脑的狂交了8次WA。。居然是更新的时候把r-l写成了l-r。。。这题就是区间更新裸题。区间更新就是加一个lazy标记,延迟标记,只有向下查询的时候才将lazy标记向下更新。其他的均按线段树的来就行。代码如下:#include<iostream>#in......
  • HDU 1166 敌兵布阵(线段树)
    题目地址:HDU1166听说胡浩版的线段树挺有名的。于是就拜访了一下他的博客。详情戳这里。于是就完全仿照着胡浩大牛的风格写的代码。至于原理,鹏鹏学长已经讲的再清晰不过了。我就在下面的代码注释中将原理说明一下吧。来纪念第一发线段树。下面是代码+注释。#include<iostream>#in......