2286: [Sdoi2011]消耗战

一道虚树模板题,虚树的写法我是写一个栈然后跑LCA的。
虚树建完之后一个树形DP就跑过去了。

#include<bits/stdc++.h>
using namespace std;

const int N = 250005 * 2;
const int K = N;
const int M = N * 3;

#define int long long

int st[N][32] , val[N][32] , cnt , stk[N] , top , node[N] , tot , lca ,depth[N] , dfn[N] , nodesize;
int fir[N] , ne[M] , to[M] , C[M] , dp[N] , dfs_clock , out[N] , root;

vector<int>G[N] , Val[N];

void add(int x ,int y ,int z) {
    ne[++ cnt] = fir[x]; fir[x] = cnt; to[cnt] = y; C[cnt] = z;
}

void link(int x ,int y,int z) {
    add(x,y,z);
    add(y,x,z);
}

#define Foreachson(i,x) for(int i = fir[x];i;i = ne[i])

void dfs(int x, int f , int fe) {
    depth[x] = depth[f] + 1;
    st[x][0] = f;
    val[x][0] = C[fe];
    dfn[x] = ++ dfs_clock;
    for(int i = 1;i <= 30;i ++) {
        st[x][i] = st[st[x][i-1]][i-1];
        val[x][i] = min(val[x][i - 1] , val[st[x][i-1]][i-1]);
    }
    Foreachson(i,x) {
        int V = to[i];
        if(V != f) {
            dfs(V,x,i);
        }
    }
    out[x] = ++ dfs_clock;
}

int LCA_no(int x ,int y) {
    if(x == y) return x;
    if(depth[x] > depth[y]) swap(x,y);
    for(int i = 30;i >= 0;i --) {
        if(depth[st[y][i]] >= depth[x]) y= st[y][i];
    }
    if(x == y) return x;
    for(int i = 30;i >= 0;i --) {
        if(st[y][i] != st[x][i]) {
            y = st[y][i];
            x = st[x][i];
        }
    }
    return st[x][0];
}

int LCA_val(int x ,int y) {
    if(x == y) while(1) cerr <<"Wrong Answer!"<<endl;
    int res = 2e9;
    if(depth[x] > depth[y]) swap(x,y);
    for(int i = 30;i >= 0;i --) {
        if(depth[st[y][i]] >= depth[x]) {
            res = min(res,val[y][i]);
            y = st[y][i];
        }
    }
    if(x == y) return res;
    for(int i = 30;i >= 0;i --) {
        if(st[y][i] != st[x][i]) {
            res = min(res,val[y][i]);
            y = st[y][i];
            res = min(res,val[x][i]);
            x = st[x][i];
        }
    }
    return min(res , min(val[x][0],val[y][0]));
}

bool check(int x ,int y) {
    return (dfn[x] <= dfn[y] && out[x] >= out[y]);
}

bool cmp(int x ,int y) {
    return dfn[x] < dfn[y];
}

int vis[N];

void newnode(int x ,int t) {
    if(vis[x] == t) return;
    vis[x] = t;
    G[x].clear();
    Val[x].clear();
}

void newadd(int x ,int  y,int z) {
    G[x].push_back(y);
    Val[x].push_back(z);
    G[y].push_back(x);
    Val[y].push_back(z);
}

int List[N * 2];

int in[N];

void dop(int x ,int f , int t) {
    dp[x] = 0;
    for(int i = 0;i <(int) G[x].size();i ++) {
        int V = G[x][i] , F = Val[x][i];
        if(V == f) continue;
        dop(V,x,t);
        if(in[V] == t) dp[x] += F;
        else dp[x] += min(dp[V] , F);
    }
}

void build(int t) {
    int len;
    len = 0;
    scanf("%lld",&tot);
    List[++ len] = 1;
    for(int i = 2;i <= tot + 1;i ++) scanf("%lld",&List[i]) , in[List[i]] = t;
    len = tot + 1;
    //果然是要sort两遍。。。 
    sort(List + 1, List + len + 1, cmp);

    for(int i = 1;i <= tot;i ++) {
        List[++ len] = LCA_no(List[i] , List[i + 1]);
    }

    sort(List + 1, List + len + 1, cmp);
    len = unique(List + 1,List + len +1) - List - 1;
    for(int i = 1;i <= len  ;i ++) {
        G[List[i]].clear();
        Val[List[i]].clear();
    }

    top = 0;
    for(int i = 1;i <= len;i ++) {
        while(top > 0 && !check(stk[top],List[i])) top --;
        if(!top) root = List[i];
        else {
            newadd(stk[top] , List[i] , LCA_val(stk[top],List[i]));
        }
        stk[++ top] = List[i];
    }

    for(int i = 1;i <= len;i ++) {
        dp[List[i]] = 1e18 + 666;
    }

    dop(1,0,t);
    printf("%lld\n",dp[1]);
}

int n , m;

signed main() {
    scanf("%lld",&n);
    for(int i = 1 , x, y, z;i <= n- 1;i ++) {
        scanf("%lld%lld%lld",&x,&y,&z);
        link(x,y,z);
    }
    dfs(1,0,0);
    scanf("%lld",&m);
    for(int i = 1;i <= m;i ++) build(i);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值