bzoj3083: 遥远的国度

27 篇文章 0 订阅
14 篇文章 0 订阅

题面在这里

题意:

给一棵树,每个点上有点权。
三种操作:
1.换根
2.链覆盖
3.以当前的根查询某子树内的最小值

做法:

当然不用真的去换根= =
可以直接令任意一点为根,比如1。
发现一条链是不会发生改变的,于是链覆盖操作可以直接树剖+线段树修改。
对于查询操作,我们分类讨论。
1.假如当前根和x相同,就直接取整棵树的最小值。
2.假如当前根在x的子树(以1为根)内,答案应该等于(全局)除去(x到当前根路径上x的儿子的子树)部分的最小值。
3.否则就是x以1为根的子树内最小值。
于是我们用大法师序维护就解决了。

代码:

/*************************************************************
    Problem: bzoj 3083 遥远的国度
    User: fengyuan
    Language: C++
    Result: Accepted
    Time: 4232 ms
    Memory: 20116 kb
    Submit_Time: 2018-01-10 14:31:03
*************************************************************/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#define mid (l+r>>1)
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;

inline LL read()
{
    char ch = getchar(); LL x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = x*10 + ch-'0';
    return x*op;
}
inline void write(LL a) { if(a < 0) putchar('-'); if(a >= 10) write(a/10); putchar(a%10+'0'); }

const int N = 100010;
int n, m, cnt, rt, clk;
int head[N], a[N], sz[N], son[N], depth[N], f[N][19], in[N], out[N], top[N];
int mi[N<<2], tag[N<<2];
struct Edge{
    int to, nxt;
    Edge() {}
    Edge(int x, int y) { to = x, nxt = y; }
}e[N<<1];

inline void addEdge(int x, int y) { e[++ cnt] = Edge(y, head[x]); head[x] = cnt; }
inline void dfs(int u, int lst, int s)
{
    depth[u] = s; f[u][0] = lst; sz[u] = 1;
    for(int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to; if(v == lst) continue;
        dfs(v, u, s+1); sz[u] += sz[v];
        if(!son[u] || sz[v] > sz[son[u]]) son[u] = v;
    }
}
inline void insert(int o, int l, int r, int x, int y)
{
    if(l == r) { mi[o] = y; return; }
    if(x <= mid) insert(lc, l, mid, x, y);
    else insert(rc, mid+1, r, x, y);
    mi[o] = min(mi[lc], mi[rc]);
}
inline void dfs2(int u, int t)
{
    in[u] = ++ clk; top[u] = t;
    insert(1, 1, n, in[u], a[u]);
    for(int i = 1; i <= 17; i ++) f[u][i] = f[f[u][i-1]][i-1];
    if(son[u]) dfs2(son[u], t);
    for(int i = head[u]; i; i = e[i].nxt)
        if(e[i].to != son[u] && e[i].to != f[u][0]) dfs2(e[i].to, e[i].to);
    out[u] = clk;
}
inline void pushdown(int o)
{
    if(!tag[o]) return;
    tag[lc] = tag[rc] = 1;
    mi[lc] = mi[rc] = mi[o];
    tag[o] = 0;
}
inline void cover(int o, int l, int r, int x, int y, int z)
{
    if(l == x && r == y) { mi[o] = z; tag[o] = 1; return; }
    pushdown(o);
    if(y <= mid) cover(lc, l, mid, x, y, z);
    else if(x > mid) cover(rc, mid+1, r, x, y, z);
    else cover(lc, l, mid, x, mid, z), cover(rc, mid+1, r, mid+1, y, z);
    mi[o] = min(mi[lc], mi[rc]);
}
inline void update(int x, int y, int z)
{
    while(top[x] != top[y]) {
        if(depth[top[x]] < depth[top[y]]) swap(x, y);
        cover(1, 1, n, in[top[x]], in[x], z);
        x = f[top[x]][0];
    }
    if(depth[x] > depth[y]) swap(x, y);
    cover(1, 1, n, in[x], in[y], z);
}
inline int query(int o, int l, int r, int x, int y)
{
    if(l == x && r == y) return mi[o];
    pushdown(o);
    if(y <= mid) return query(lc, l, mid, x, y);
    else if(x > mid) return query(rc, mid+1, r, x, y);
    else return min(query(lc, l, mid, x, mid), query(rc, mid+1, r, mid+1, y));
}
inline int find(int x, int y)
{
    int t = y, tmp = depth[y] - depth[x] - 1;
    for(int i = 17; i >= 0; i --)
        if(tmp>>i&1) y = f[y][i];
    return y;
}
inline bool isAncestor(int x, int y) { return in[x] <= in[y] && out[x] >= out[y]; }
inline int qmin(int x)
{
    if(x == rt) return mi[1];
    else if(isAncestor(x, rt)) {//x is the ancestor of rt
        int t = find(x, rt), xxx = 1e9;
        if(in[t]-1 >= 1) xxx = min(xxx, query(1, 1, n, 1, in[t]-1));
        if(out[t]+1 <= n) xxx = min(xxx, query(1, 1, n, out[t]+1, n));
        return xxx;
    } else return query(1, 1, n, in[x], out[x]);
}
//debug
inline void printTree(int o, int l, int r)
{
    printf("%d %d: %d\n", l, r, mi[o]);
    if(l == r) return;
    printTree(lc, l, mid); printTree(rc, mid+1, r);
}
int main()
{
    n = read(), m = read();
    for(int i = 1; i < n; i ++) {
        int x = read(), y = read();
        addEdge(x, y); addEdge(y, x);
    }
    for(int i = 1; i <= n; i ++) a[i] = read();
    memset(mi, 0x3f, sizeof mi);
    dfs(1, 0, 0); dfs2(1, 1);
    rt = read();
    while(m --) {
        int opt = read(), x, y, z;
        if(opt == 1) rt = read();
        else if(opt == 2) {
            x = read(), y = read(), z = read(); update(x, y, z);
        } else {
            x = read(); write(qmin(x)), puts("");
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值