多校第一场1006


题目描述:

给出一棵树,n~1e5,然后给出m~1e5条特殊的链,每个链有一个权值.现在在树上挑选不相交的链,使得最后的拿到的权值最大.

题解:

看一个点,是树形dp,但是发现如果u是root,枚举过u的链,发现要暴力求和链上周围的点的dp和.这样会超时. 于是想到快速求一条链的和. 为了好写,我们把求链上连接的儿子的dp值的和转化到链上的权值. sum[u]指u的所有儿子的dp和,这个好想,那么一条链上的其实就是(sum[u] - 所有链上儿子d+链上儿子的sum-链上儿子儿子的d….),总结就是链上除了root,其他的点权值都是sum[v]-d[v].而root的权值是sum[root]. 我们是算d[u]的时候才用的,当时d[u]肯定就是0. 所以求链上的权值和. 我们有两种求法
(1)邓爷教的标序+lca. 这样求的是路径,我们用一种常用的方法,把i的值归到i到他fa的那条边上. 这样我们求路径+sum[root]就行了.
(2)用树链剖分. 映射到树状数组上,每次求和.

重点:

(1)关键是求一条链上的和.
(2)把权值归到链上,简化写法.
(3)点的权值归到u到fa的边上.
(4)标序或者树链剖分求链的和
(5)标序+st表求lca

代码:
//这个是标序算链的和.
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const int maxn = 1e5 + 100;
const int maxRMQ = 4e5 + 100;
const int maxIND = 2e5 + 1000;
const int maxTree = 1e6 + 100;
int nn, m;
struct Edge
{
    int a, b, val;
};
vector<Edge> edge[maxn];
vector<int> G[maxn];
int st[maxRMQ][50], L2[maxRMQ], P2[50];

int dfn, index[maxn], fa_index[maxn], first_u[maxn];
int a_st[maxRMQ], an;

int dfx, getin[maxn], getout[maxn];
int d[maxn], sum[maxn];

//void pushUp(int rt)
//{
//    int lRt = (rt<<1), rRt = ((rt<<1)|1);
//    tree[rt] = tree[lRt]+tree[rRt];
//}
//void change(int pos, int key, int rt, int l, int r)
//{
//    if(l==r && l == pos)//主义同时修改tree
//    {
//        tree[rt] = key;
//        return;
//    }
//    pushDown(rt);
//    int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
//    if(pos <= mid)
//    {
//        change(pos,key, lRt, l, mid);
//    }
//    if(pos >= mid + 1)
//    {
//        change(pos, key, rRt, mid + 1, r);
//    }
//    pushUp(rt);//向上push
//}
//
//int query(int L, int R, int rt, int l, int r)
//{
//    if(L <= l && R >= r)//全包括
//    {
//        return tree[rt];
//    }
//    int ans =0, mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
//    if(L <= mid)
//    {
//        ans += query(L, R, lRt, l, mid);
//    }
//    if(R >= mid + 1)
//    {
//        ans += query(L, R, rRt, mid + 1, r);
//    }
//    return ans;
//}


void dfs_dfn(int u, int fa)//这是lca
{
    dfn++;
    index[u] = dfn;
    fa_index[dfn] = u;
    an++;
    a_st[an] = dfn;
    first_u[u] = an;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=fa)
        {
            dfs_dfn(v, u);
            an++;
            a_st[an] = index[u];
        }
    }
}
void getL2()
{
    L2[1] = 0;
    for(int i = 2; i<=400000; i++)
    {
        if((i&(i-1))==0)
        {
            L2[i] = L2[i-1]+1;
        }
        else
        {
            L2[i] = L2[i-1];
        }
        //printf("%d %d\n", i, L2[i]);
    }
}
void initail()
{
    P2[0] = 1;
    for(int i = 1; i<=30; i++)
    {
        P2[i] = 2*P2[i-1];
    }

    for(int i = 1; i <= an; i++)
    {
        st[i][0] = a_st[i];
        //printf(" i is %d  %d\n", i, a_st[i]);
    }
    for(int s = 1; s<=30; s++)
    {
        for(int i = 1; i+P2[s] - 1 <=an; i++)
        {
            int j = i+P2[s-1];
            st[i][s] = min(st[i][s-1], st[j][s-1]);
        }
    }
}
int st_query(int a, int b)
{
    int len = (b-a+1);
    int s = L2[len];
    return min(st[a][s], st[b-P2[s]+1][s]);
}
int getLca(int a, int b)//lca结束
{
    int l = first_u[a], r = first_u[b];
    if(l > r)
    {
        swap(l, r);
    }
    return fa_index[st_query(l, r)];
}

int tree[maxn << 1];//树状数组
int getsum(int x)
{
    int ret = 0;
    while (x)
    {
        ret += tree[x];
        x -= (x & (-x));
    }
    return ret;
}
void update(int x, int y)
{
    while (x <= dfx)
    {
        tree[x] += y;
        x += (x & (-x));
    }
}


void dfs_xu(int u, int fa)
{
    dfx++;
    getin[u] = dfx;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=fa)
        {
            dfs_xu(v, u);
        }
    }
    dfx++;
    getout[u] = dfx;
}

int getT(int a, int b)
{
    if(a==1)
    {
        return getsum(b);
    }
    return getsum(b)-getsum(a-1);
}

int getSum_t(int a, int b, int lca)
{
    int l = getin[lca];
    int r = getin[a];
    int ans = 0;
    ans += getT(l, r);
    r = getin[b];
    ans += getT(l, r);
    return ans;
}

void dfs(int u, int fa)//树形dp,求和链上.
{
    sum[u] = 0;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=fa)
        {
            dfs(v, u);
            sum[u] += d[v];
        }
    }
    d[u] = sum[u];
    REP(i, 0, edge[u].size())
    {
        Edge e = edge[u][i];
        int a = e.a, b = e.b, val = e.val;
//        if(u == 2)
//        {
//            for(int i = 1; i<=dfx; i++)
//            {
//                printf("tree i is %d  %d\n", i, tree[i]);
//            }
//        }
        int tmp = getSum_t(a, b, u);
        d[u] = max(d[u], tmp + sum[u] + val);
    }
    //printf("%d  %d %d\n", u, sum[u], d[u]);
    update(getin[u], sum[u]-d[u]);
    update(getout[u], d[u]-sum[u]);

    //change(getin[u], sum[u]-d[u], 1, 1, dfx);
    //change(getout[u], d[u]-sum[u], 1, 1, dfx);
}
void solve()
{
    REP_D(i, 1, nn)
    {
        G[i].clear();
        edge[i].clear();
    }
    REP_D(i, 1, nn - 1)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        G[a].push_back(b);
        G[b].push_back(a);
    }
    dfn = 0;
    an = 0;
    dfs_dfn(1, 0);
    initail();
    REP_D(i, 1, m)
    {
        int a, b, val;
        scanf("%d%d%d", &a, &b, &val);
        int lca = getLca(a, b);
        Edge t;
        t.a = a;
        t.b = b;
        t.val = val;
        edge[lca].push_back(t);
        //printf("lca %d %d %d\n", a, b, lca);
    }
    CLR(tree);
    CLR(sum);
    CLR(d);
    dfx = 0;
    dfs_xu(1,0);
    dfs(1, 0);
    printf("%d\n", d[1]);
}

int main()
{
   // freopen("13Min.txt", "r", stdin);
    //freopen("1out.txt", "w", stdout);
    int t;
    getL2();
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &nn,&m);
        solve();
    }
    return 0;
}

//这个是树链剖分,但是线段树要re....所以没有过
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const int maxn = 1e5 + 100;
const int maxLMQ = 4*maxn+100;
const int MAXL = 30;
int st[maxLMQ][MAXL];
int an, a[maxLMQ];
int index[maxn], fa_index[maxn], first_byindex[maxn];
int dfn;
vector<int> G[maxn];

struct Edge
{
    int a, b, val, lca;
};
Edge edge[maxn];
vector<Edge> lca_edge[maxn];
int edge_n;

int P2[maxn], L2[maxn];

int d[maxn], sum[maxn];
int dep[maxn], w[maxn], fa[maxn], top[maxn], son[maxn], siz[maxn];
int z;
int tree[maxLMQ];

int n;

void getP2()
{
    P2[0] = 1;
    for(int i = 1; i<=30; i++)
    {
        P2[i] = P2[i-1]*2;
    }
}
void getL2()
{
    L2[1] = 0;
    for(int i = 2; i<=400000; i++)
    {
        if((i&(i-1))==0)
        {
            L2[i] = L2[i-1]+1;
        }
        else
        {
            L2[i] = L2[i-1];
        }
        //printf("%d %d\n", i, L2[i]);
    }
}

void initail()
{
    for(int i = 1; i <= an; i++)
    {
        st[i][0] = a[i];
    }
    for(int s = 1; s<=30; s++)
    {
        for(int i = 1; i<= an; i++)
        {
            int j = i+P2[s-1];
            if(j>=an)
                continue;
            st[i][s]= min(st[i][s-1],st[j][s-1]);
        }
    }
}
int query(int l, int r)
{
    int len = (r-l+1);
    int s = L2[len];
    int tmp = P2[s];
    return min(st[l][s], st[r - tmp + 1][s]);
}
int getLca(int a, int b)
{
    a = index[a];
    b = index[b];
    int l = first_byindex[a], r = first_byindex[b];
    if(l > r)
    {
        swap(l, r);
    }
    return fa_index[query(l, r)];
}

void dfs_dfn(int u, int fa)
{
    ++an;
    a[an] = dfn;
    first_byindex[dfn] = an;
    index[u] = dfn;
    fa_index[dfn] = u;
    dfn++;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=fa)
        {
            dfs_dfn(v, u);
            an++;
            a[an] = index[u];
        }
    }
}

void getEdge()
{
    REP_D(i, 1, n)
    {
        lca_edge[i].clear();
    }
    REP(i, 0, edge_n)
    {
        scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].val);
        edge[i].lca = getLca(edge[i].a, edge[i].b);
        lca_edge[edge[i].lca].push_back(edge[i]);
    }
}

void dfs_1(int u, int pat)//先准备
{
    siz[u] = 1;
    son[u] = 0;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=pat)
        {
            fa[v] = u;
            dep[v] = dep[u]+1;
            dfs_1(v, u);
            if(son[u]==0)
            {
                son[u] = v;
            }
            else if(siz[v] > siz[son[u]])
            {
                son[u] = v;
            }
            siz[u] += siz[v];
        }
    }
}

void dfs_2(int u, int pat, int tp)//标号边
{
    if(pat != 0)
    {
        z++;
        w[u] = z;
    }
    top[u] = tp;
    if(son[u] !=0)
    {
        dfs_2(son[u], u, top[u]);
    }
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=pat&&v!=son[u])
        {
            dfs_2(v, u, v);
        }
    }
}

int query_tree(int LL, int RR, int rt, int l, int r)
{
    if(LL<=l && RR>= r)
    {
        return tree[rt];
    }
    int ans = 0;
    int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
    if(LL<=mid)
    {
        ans += query_tree(LL, RR, lRt, l, mid);
    }
    if(RR>=mid+1)
    {
        ans += query_tree(LL, RR, rRt, mid+1, r);
    }
    return ans;
}
void pushUp(int rt)
{
    int lRt = (rt<<1), rRt = ((rt<<1)|1);
    tree[rt] = tree[lRt]+ tree[rRt];
}
void update(int pos, int x, int rt, int l, int r)
{
    if(pos == l && pos == r)
    {
        tree[rt] = x;
        return;
    }
    int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
    if(pos <= mid)
    {
        update(pos, x, lRt, l, mid);
    }
    else
    {
        update(pos , x, rRt, mid +1, r);
    }
    pushUp(rt);
}

int getSum(int a, int b)
{
    int f1 = top[a], f2 = top[b];
    int ans = 0;
    while(f1 != f2)
    {
        if(dep[f1] < dep[f2])
        {
            swap(f1, f2);
            swap(a, b);
        }
        ans += (query_tree(w[f1], w[a], 1, 1, z));
        a = fa[f1];
        f1 = top[a];
    }
    if(a==b)
        return ans;
    if(dep[a] > dep[b])
    {
        swap(a, b);
    }
    ans += query_tree(w[son[a]], w[b], 1, 1, z);
    return ans;
}

void dfs(int u, int pat)
{
    sum[u]= 0;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=pat)
        {
            dfs(v, u);
            sum[u] += d[v];
        }
    }
    d[u] = sum[u];
    REP(i, 0, lca_edge[u].size())
    {
        Edge &e = lca_edge[u][i];
        int a = e.a, b = e.b, val = e.val;
        int temp = getSum(e.a, e.b);
        d[u]=max(d[u], temp+sum[u]+val);
    }
    if(u!=1)
    {
        update(w[u], sum[u]-d[u], 1, 1, z);
    }
    //printf("%d %d  %d \n", u, sum[u], d[u]);
}

void solve()
{
    CLR(tree);
    dfn = 1;
    an = 0;
    dfs_dfn(1, 0);
    initail();
    getEdge();
    dep[1] = 0;
    dfs_1(1, 0);
    z = 0;
    dfs_2(1, 0, 1);
    dfs(1, 0);
    printf("%d\n", d[1]);
}

int main()
{
    //freopen("6Fin.txt", "r", stdin);
    //freopen("6Fout.txt", "w", stdout);
    getP2();
    getL2();
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &edge_n);
        REP_D(i, 1, n)
        {
            G[i].clear();
        }
        REP_D(i, 1, n-1)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值