BZOJ 3991(dfs序 + LCA 公式推导)

题意:

给定一颗带权的树(n<=1e5),维护一个动态的集合(即插入点和删除点),求从其中一点出发到达所有点并返回的最小距离。

分析:

到达所有点并返回,是这些点用最少的边连起来的权值的2倍。这样只需动态计算,最少边的和。

这里以1为根节点,跑一遍dfs序。

考虑插入:那么如果插入点得dfs序,在集合中存在点dfs序的中间,找到比之小的最大点,和比之大的最小点,因为插入点在内部这两点连线与该点得距离最小。

否则,插入点在外部,找dfs序最大和最小点,即可。

删除是插入的逆操作。

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <list>
#include <cstdlib>
#include <queue>
#include <stack>
#include <cmath>
#include <bitset>
#include <cassert>
#define ALL(a) a.begin(), a.end()
#define clr(a, x) memset(a, x, sizeof a)
#define X first
#define Y second
#define pb push_back
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define rep1(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) for(int i=0;i<(int)n;i++)
using namespace std;
const double eps = 1e-10;
typedef long long LL;
typedef long long ll;
typedef pair<int, int> pii;
const int oo =0x3f3f3f3f;

const int maxn=100000+100;
const int DEG=30;

struct Edge
{
    int v, nxt , cap;
} edge[maxn<<1];

int n, head[maxn], tot, fa[maxn][DEG], deg[maxn], sz[maxn];

void init()
{
    clr(head, -1);
    tot=0;
}

void AddEdge(int u, int v ,int c)
{
    edge[tot].v=v;
    edge[tot].cap=c;
    edge[tot].nxt=head[u];
    head[u]=tot++;
}

void dfs(int u)
{
    for(int i=1; i<DEG; i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    sz[u]=1;
    for(int i=head[u]; ~i; i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(v==fa[u][0])continue ;
        deg[v]=deg[u]+1;
        fa[v][0]=u;
        dfs(v);
        sz[u]+=sz[v];
    }
}

int par(int u, int det)
{
    for(int i=0; det; det>>=1, i++)
        if(det&1)
            u=fa[u][i];
    return u;
}

int LCA(int u, int v)
{
    if(deg[u]>deg[v])swap(u, v);
    int tu=u, tv=v;
    tv=par(v, deg[v]-deg[u]);
    if(tu==tv)return tu;
    for(int i=DEG-1; i>=0; i--)
    {
        if(fa[tu][i]==fa[tv][i])continue ;
        tu=fa[tu][i], tv=fa[tv][i];
    }
    return fa[tu][0];
}
int id[maxn] , cnt_=0 ;
ll d[maxn];
void init_dfs(int u,int f , ll len)
{
    id[u] = ++cnt_;
    d[u] = len;
    for(int i=head[u]; ~i; i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(v==f)continue ;
        init_dfs(v,u,len+edge[i].cap);
    }
}
struct node
{
    int id,u;
    node(int u=0,int id=0):u(u),id(id) {}
    bool operator<(const node& rhs)const
    {
        return id < rhs.id;
    }
};
set<node> que;
typedef set<node>::iterator set_p;
ll now = 0;
void cal(int u)
{
    if(que.empty()) {
          que.insert(node(u,id[u])); return ;
    }
    set_p p = que.find(node(u,id[u])) , p1, p2;
    int flag = -1;
    if(p == que.end())
    {
        que.insert(node(u,id[u]));
        flag = 1;
    }
     p = que.find(node(u,id[u]));
     p1 = p2 = p;  set_p ed = que.end(); --ed;
     if(p == que.begin()) p1++,p2=ed;
     else if(p == ed) p1=que.begin(),p2=ed,--p2;
     else p1--, p2++;
     int y = p2->u;
     int x = p1->u;
     now+=((ll)d[u]-d[LCA(x,u)]-d[LCA(y,u)]+d[LCA(x,y)])*flag;
    if(flag == -1) que.erase(p);
}
int Q;
int main()
{
    scanf("%d %d", &n ,&Q);
    init();
    for(int i=0; i<n-1; i++)
    {
        int u, v , c;
        scanf("%d%d%d", &u, &v,&c);
        AddEdge(u, v , c);
        AddEdge(v, u , c);
    }
    deg[1]=0;
    fa[1][0]=1;
    dfs(1);
    cnt_ = 0; d[1] = 0;
    init_dfs(1,-1,0);
    que.clear();
    now = 0;
    while(Q--)
    {
        int u;
        scanf("%d",&u);
        cal(u);
        printf("%lld\n",now*2);
    }
}


你可能感兴趣的:(BZOJ 3991(dfs序 + LCA 公式推导))