zoukankan      html  css  js  c++  java
  • [SDOI2011]消耗战

    题意

    Here

    简要题意:给定一颗树,树边带权,给 (m) 个询问,每次给 (k) 个点,询问删除若干条边使得这 (k) 个点都不与 (1) 号点联通的最小代价。(sum k_i leq500000,1leq m)

    思考

    同时也记录一下虚树的学习。

    看完题目,第一时间想到树形 (dp),每次 (O(n)),总复杂度 (O(nm)),但是这个 (m) 的数据范围太迷了,肯定 (TTTTTLE),题中又给了 (sum k) 的数据范围,我们难道要从 (k) 下手吗?

    考虑每次树形 (dp),我们每次要遍历 (n) 个点,但是显然每次有用的只有 (k) 个点,我们能否只利用这 (k) 个点建出一颗小树,这样的话每次 (dp) 的复杂度是 (O(k_i)),总复杂度 (O(sum k_i))

    虚树可以解决这个问题,不过我们发现,两点间需要靠它们的 (lca) 产生联系,那么虚树中还要存在 (lca),我们发现每加入一个点,至多新加入一个 (lca),那么虚树中点的上界是 (2 * k)(dp)的理论总复杂度的上界是 (O(2 * sum k_i))

    关于虚树的建立,首先我们处理出各个点的 (dfn) 序,将询问点按 (dfn) 从小到大排序。
    我们还有一个栈(默认根节点为 (1),那么栈底元素为 (1)),这个栈储存的是从 (1) 号点到 栈顶元素 的链的信息。考虑每次插入一个询问点,设(x = S[top], y = S[top - 1]),插入点为 (u)

    1. (lca(x, u) = x)(u)(x) 的子节点,那么 (u) 还在同一条链上,直接插入。
    2. (lca(x, u) != x),那么 (LCA)(x) 这条链已经遍历完了,我们将这条子链上的点连边(具体按 (dfn) 序判断),再插入 (u)

    以上只是一个简略的过程,具体可以看代码并手动模拟一下,其实很简单。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    inline ll read(){
        ll x=0, f=1; char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x * f;
    }
    const ll N = 250050;
    const ll oo = 0x3f3f3f3f3f3f3f3f;
    struct node{
        ll nxt, to, dis;
    }edge[N << 1];
    ll head[N], num;
    void build(ll from, ll to, ll dis){
        edge[++num].nxt = head[from];
        edge[num].to = to;
        edge[num].dis = dis;
        head[from] = num;
    }
    ll d[N], MIN[N], f[N][20], dfn[N], dfs_clock;
    void dfs(ll u, ll fa){
        dfn[u] = ++ dfs_clock;
        for(ll i=head[u]; i; i=edge[i].nxt){
            ll v = edge[i].to, dist = edge[i].dis;
            if(v == fa) continue;
            d[v] = d[u] + 1;
            f[v][0] = u;
            MIN[v] = min(MIN[u], dist);
            dfs(v, u);
        }
    }
    ll lca(ll u, ll v){
        if(d[u] < d[v]) swap(u, v);
        for(ll i=18; i>=0; i--) if(d[f[u][i]] >= d[v]) u = f[u][i];
        if(u == v) return u;
        for(ll i=18; i>=0; i--) if(f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
        return f[u][0];
    }
    vector<ll> G[N];
    void add(ll u, ll v){ G[u].push_back(v); }
    ll S[N], top;
    void insertx(ll u){
        if(top == 1) { S[++top] = u; return; }
        ll LCA = lca(u, S[top]);
        if(LCA == S[top]) return;
        while(top > 1 && dfn[S[top - 1]] >= dfn[LCA]) add(S[top - 1], S[top]), top --;
        if(S[top] == LCA) S[++top] = u;
        else add(LCA, S[top]), S[top] = LCA, S[++top] = u;
    }
    ll ans[N];
    void dp(ll u){
        ans[u] = MIN[u];
        if(G[u].size() == 0) return;
        ll sum = 0;
        for(ll i=0; i<G[u].size(); i++){
            ll v = G[u][i];
            dp(v);
            sum += ans[v];
        }
        ans[u] = min(ans[u], sum);
        G[u].clear();
    }
    ll ask[N];
    bool cmp(ll x, ll y){
        return dfn[x] < dfn[y];
    }
    ll n, m;
    int main(){
        n = read();
        for(ll i=1; i<=n-1; i++){
            ll u = read(), v = read(), d = read();
            build(u, v, d); build(v, u, d);
        }
        memset(MIN, 0x3f, sizeof(MIN)); d[1] = 1;
        dfs(1, 0);
        for(ll j=1; j<=18; j++)
            for(ll i=1; i<=n; i++) f[i][j] = f[f[i][j-1]][j-1];
        m = read();
        while(m --){
            S[1] = 1; top = 1;
            ll k = read();
            for(ll i=1; i<=k; i++) ask[i] = read();
            sort(ask + 1, ask + k + 1, cmp);
            for(ll i=1; i<=k; i++) insertx(ask[i]);
            while(top > 1) add(S[top-1], S[top]), top --;
            dp(1);
            cout << ans[1] << endl;
        }
        return 0;
    }
    

    总结

    由于每次建虚树之后要清空边,注意只清空询问的点的边。

    数据范围没标清的话记得开 (long long)

  • 相关阅读:
    django模板语言之Template
    python基础知识回顾之字符串
    在命令行中创建Django项目
    Ubuntu如何开启SSH服务
    跨过Django的坑
    python基础知识回顾之元组
    python基础知识回顾之列表
    SQL Server登录方式
    k8s或者docker容器在安装服务之后,用systemctl启动会报错:connection: Operation not permitted
    记一次oracle视图查询失效的情况,ERROR at line 1: ORA-04045: errors during recompilation/revalidation of NC633.BB_API_BUDGET_EXEINFO ORA-16000: database open for read-only access
  • 原文地址:https://www.cnblogs.com/alecli/p/10105476.html
Copyright © 2011-2022 走看看