[HDU 5296] Annoying problem (DFS序性质+LCA)

HDU - 5296

一棵树上有若干个点,每条边有一个边权

给一个初始为空的集合,每次向集合内添加一个点或者删除一个点

问每次操作结束后,将集合内所有点连起来的边权和为多少



假设集合内已经有一些点,那么再加一个点所增加的边权

将会是这个点到某一条链的距离

但是这条链不能随便挑选,否则可能会经过已经选择的边

挑选策略就是,找到集合内dfs序比当前点大和小的两个点组成的链

换句话来说,就是集合内dfs序最靠近当前点的两个点

设当前点 x,这样选链,选出的链是 (u,v),并且设dfsn[u]<dfsn[v]

如果会经过已经选择的边,已经选择的点 y

那么,如果原树dfs是从 x->y的,那么 x就比 y的dfs序小

而 y比 u、v的dfs序都小,所以原树的dfs是从 y->x的

而这样一来,y的dfs序就比 u更靠近 x,同样不可能

所以,这样选链,x到链的路径上不会经过重复的边

删除的时候基本相同

设len(u,v)为链(u,v)的长度

树上一点 x到链 (u,v)的距离为

len(u,x)+len(v,x)?len(u,v)2

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))

const int maxn=1e5+10, maxq=maxn;
struct Graph
{
    int ndn, edn, last[maxn];
    int u[2*maxn], v[2*maxn], w[2*maxn], nxt[2*maxn];
    void init(int tn){ ndn=tn; edn=0; MST(last,-1);}
    void adde(int tu, int tv, int tw)
    {
        u[edn]=tu; v[edn]=tv; w[edn]=tw;
        nxt[edn]=last[tu];
        last[tu]=edn++;
    }
};

struct LCA
{
    Graph *G;
    int st[20][2*maxn]; // dfsn
    int dfst, stp, firp[maxn], dfsn[2][maxn], dist[maxn];
    void init(Graph*);
    int dfs(int);
    int get(int,int);
};

int N,Q;
Graph G;
LCA lca;
set<int> have;
bool intree[maxn];

int query(int);

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    int T;
    scanf("%d", &T);
    for(int ck=1; ck<=T; ck++)
    {
        scanf("%d%d", &N, &Q);
        G.init(N);
        for(int i=1; i<N; i++)
        {
            int u,v,w;
            scanf("%d%d%d", &u, &v, &w);
            G.adde(u,v,w); G.adde(v,u,w);
        }
        lca.init(&G);
        have.clear();

        CLR(intree);
        int ans=0;
        printf("Case #%d:\n", ck);
        for(int i=0; i<Q; i++)
        {
            int opt, x;
            scanf("%d%d", &opt, &x);
            if(opt==1)
            {
                if(!intree[x])
                {
                    ans += query(x);
                    have.insert(lca.dfsn[0][x]);
                    intree[x]=1;
                }
            }
            else
            {
                if(intree[x])
                {
                    have.erase(lca.dfsn[0][x]);
                    ans -= query(x);
                    intree[x]=0;
                }
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}

int query(int x)
{
    int u,v,res;
    set<int>::iterator it;
    int *dist=lca.dist;
    if(!have.size()) res=0;
    else
    {
        it=have.lower_bound(lca.dfsn[0][x]);
        if(it==have.begin() || it==have.end())
        {
            u = lca.dfsn[1][*have.begin()];
            v = lca.dfsn[1][*have.rbegin()];
        }
        else
        {
            u = lca.dfsn[1][*it];
            it--;
            v = lca.dfsn[1][*it];
        }
        res = dist[x] + dist[lca.get(u,v)] - dist[lca.get(x,u)] - dist[lca.get(x,v)];
    }
    return res;
}

void LCA::init(Graph *g)
{
    G=g;
    dfst = stp = 0;
    CLR(firp); CLR(dfsn); CLR(dist);
    dfs(1);

    int Lim=log2(stp);
    for(int j=1; j<=Lim; j++)
    {
        int len=1<<j-1, nlim=stp-(1<<j)+1;
        for(int i=1; i<=nlim; i++)
            st[j][i] = min(st[j-1][i], st[j-1][i+len]);
    }
}

int LCA::dfs(int u)
{
    dfsn[0][u] = ++dfst;
    st[0][++stp] = dfst;
    firp[u] = stp;
    dfsn[1][dfst] = u;

    for(int e=G->last[u]; ~e; e=G->nxt[e])
    {
        int v=G->v[e];
        if(dfsn[0][v]) continue;
        dist[v] = dist[u] + G->w[e];
        dfs(v);
        st[0][++stp] = dfsn[0][u];
    }
    return 0;
}

int LCA::get(int x, int y)
{
    int l=firp[x], r=firp[y];
    if( r<l ) swap(l,r);
    int flr = log2( r-l+1 );
    int nlen = 1<<flr;
    return dfsn[1][ min(st[flr][l], st[flr][r-nlen+1]) ];
}/*
HDU - 5296
一棵树上有若干个点,每条边有一个边权
给一个初始为空的集合,每次向集合内添加一个点或者删除一个点
问每次操作结束后,将集合内所有点连起来的边权和为多少

假设集合内已经有一些点,那么再加一个点所增加的边权
将会是这个点到某一条链的距离

但是这条链不能随便挑选,否则可能会经过已经选择的边
挑选策略就是,找到集合内dfs序比当前点大和小的两个点组成的链
换句话来说,就是集合内dfs序最靠近当前点的两个点

设当前点 x,这样选链,选出的链是 (u,v),并且设dfsn[u] < dfsn[v]
如果会经过已经选择的边,已经选择的点 y
那么,如果原树dfs是从 x->y的,那么 x就比 y的dfs序小
而 y比 u、v的dfs序都小,所以原树的dfs是从 y->x的
而这样一来,y的dfs序就比 u更靠近 x,同样不可能
所以,这样选链,x到链的路径上不会经过重复的边

删除的时候基本相同

设len(u,v)为链(u,v)的长度
树上一点 x到链 (u,v)的距离为
( len(u,x) + len(v,x) - len(u,v))/2
*/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))

const int maxn=1e5+10, maxq=maxn;
struct Graph
{
    int ndn, edn, last[maxn];
    int u[2*maxn], v[2*maxn], w[2*maxn], nxt[2*maxn];
    void init(int tn){ ndn=tn; edn=0; MST(last,-1);}
    void adde(int tu, int tv, int tw)
    {
        u[edn]=tu; v[edn]=tv; w[edn]=tw;
        nxt[edn]=last[tu];
        last[tu]=edn++;
    }
};

struct LCA
{
    Graph *G;
    int st[20][2*maxn]; // dfsn
    int dfst, stp, firp[maxn], dfsn[2][maxn], dist[maxn];
    void init(Graph*);
    int dfs(int);
    int get(int,int);
};

int N,Q;
Graph G;
LCA lca;
set<int> have;
bool intree[maxn];

int query(int);

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    int T;
    scanf("%d", &T);
    for(int ck=1; ck<=T; ck++)
    {
        scanf("%d%d", &N, &Q);
        G.init(N);
        for(int i=1; i<N; i++)
        {
            int u,v,w;
            scanf("%d%d%d", &u, &v, &w);
            G.adde(u,v,w); G.adde(v,u,w);
        }
        lca.init(&G);
        have.clear();

        CLR(intree);
        int ans=0;
        printf("Case #%d:\n", ck);
        for(int i=0; i<Q; i++)
        {
            int opt, x;
            scanf("%d%d", &opt, &x);
            if(opt==1)
            {
                if(!intree[x])
                {
                    ans += query(x);
                    have.insert(lca.dfsn[0][x]);
                    intree[x]=1;
                }
            }
            else
            {
                if(intree[x])
                {
                    have.erase(lca.dfsn[0][x]);
                    ans -= query(x);
                    intree[x]=0;
                }
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}

int query(int x)
{
    int u,v,res;
    set<int>::iterator it;
    int *dist=lca.dist;
    if(!have.size()) res=0;
    else
    {
        it=have.lower_bound(lca.dfsn[0][x]);
        if(it==have.begin() || it==have.end())
        {
            u = lca.dfsn[1][*have.begin()];
            v = lca.dfsn[1][*have.rbegin()];
        }
        else
        {
            u = lca.dfsn[1][*it];
            it--;
            v = lca.dfsn[1][*it];
        }
        res = dist[x] + dist[lca.get(u,v)] - dist[lca.get(x,u)] - dist[lca.get(x,v)];
    }
    return res;
}

void LCA::init(Graph *g)
{
    G=g;
    dfst = stp = 0;
    CLR(firp); CLR(dfsn); CLR(dist);
    dfs(1);

    int Lim=log2(stp);
    for(int j=1; j<=Lim; j++)
    {
        int len=1<<j-1, nlim=stp-(1<<j)+1;
        for(int i=1; i<=nlim; i++)
            st[j][i] = min(st[j-1][i], st[j-1][i+len]);
    }
}

int LCA::dfs(int u)
{
    dfsn[0][u] = ++dfst;
    st[0][++stp] = dfst;
    firp[u] = stp;
    dfsn[1][dfst] = u;

    for(int e=G->last[u]; ~e; e=G->nxt[e])
    {
        int v=G->v[e];
        if(dfsn[0][v]) continue;
        dist[v] = dist[u] + G->w[e];
        dfs(v);
        st[0][++stp] = dfsn[0][u];
    }
    return 0;
}

int LCA::get(int x, int y)
{
    int l=firp[x], r=firp[y];
    if( r<l ) swap(l,r);
    int flr = log2( r-l+1 );
    int nlen = 1<<flr;
    return dfsn[1][ min(st[flr][l], st[flr][r-nlen+1]) ];
}
时间: 2024-10-05 00:30:45

[HDU 5296] Annoying problem (DFS序性质+LCA)的相关文章

HDU 5296 Annoying Problem 树链剖分 LCA 倍增法

HDU 5296 Annoying Problem 题目链接:hdu 5296 题意:在一棵给定的具有边权的树,一个节点的集合S(初始为空),给定Q个操作,每个操作增加或删除S中的一个点,每个操作之后输出使集合S中所有点联通的最小子树的边权和. 思路:最小子树上的节点的充要条件: 节点为(S集合中所有点的LCA)的子节点: 节点有一个子孙为S集合中的点. 那么我们给每个节点都开一个标记数组,初始为零,每加入一个节点,就把从这个节点到根节点路径上的点的值都+1,反之-1,这样通过对每个单节点值的查

HDU 5296 Annoying problem(LCA模板+树的dfs序心得)

Problem Description Coco has a tree, whose nodes are conveniently labeled by 1,2,-,n, which has n-1 edge,each edge has a weight. An existing set S is initially empty. Now there are two kinds of operation: 1 x: If the node x is not in the set S, add n

HDU 5296 Annoying problem LCA+树状数组

题解链接 Annoying problem Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 480    Accepted Submission(s): 146 Problem Description Coco has a tree, whose nodes are conveniently labeled by 1,2,-,n, w

【LCA】HDU 5296 Annoying problem

通道:http://acm.hdu.edu.cn/showproblem.php?pid=5296 题意:给一棵n个节点的树,再给q个操作,初始集合S为空,每个操作要在一个集合S中删除或增加某些点,输出每次操作后:要使得集合中任意两点互可达所耗最小需要多少权值.(记住只能利用原来给的树边.给的树边已经有向.10万个点,10万个操作) 思路: 代码: 1 #pragma comment(linker, "/STACK:102400000,102400000") 2 #include &l

HDU 5296 Annoying problem

http://acm.hdu.edu.cn/showproblem.php?pid=5296 实际上问题可以转化为点到一个连通子树的最短距离.. //Hello. I'm Peter. #pragma comment(linker, "/STACK:102400000,102400000") #include<cstdio> #include<iostream> #include<sstream> #include<cstring> #i

HDOJ 5296 Annoying problem LCA+数据结构

dfs一遍得到每一个节点的dfs序,对于要插入的节点x分两种情况考虑: 1,假设x能够在集合中的某些点之间,找到左边和右边距离x近期的两个点,即DFS序小于x的DFS序最大点,和大于x的DFS序最小的点...... 2.假设x在集合中的点某一側,则找距离x的dfs序最小和最大的点 将x插入这个集合最少要走的距离为 dist[x]-dist[LCA(left,x)]-dist[LCA(right,x)]+dist[LCA(left,right)] 删除同理 Annoying problem Tim

bzoj 2819 Nim(BIT,dfs序,LCA)

2819: Nim Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1596  Solved: 597[Submit][Status][Discuss] Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是vfleaking决定写一个玩Nim游戏的平台来坑玩家. 为了设计漂亮一点的

HDU 3887 Counting Offspring(DFS序求子树权值和)

Problem Description You are given a tree, it's root is p, and the node is numbered from 1 to n. Now define f(i) as the number of nodes whose number is less than i in all the succeeding nodes of node i. Now we need to calculate f(i) for any possible i

hdu 5877 Weak Pair dfs序+树状数组+离散化

Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Problem Description You are given a rooted tree of N nodes, labeled from 1 to N. To the ith node a non-negative value ai is assigned.An ordered pair of no