【Contest Hunter Round #56】异象石(lca)

第一行有一个整数 N,表示点的个数;

接下来 N-1 行每行三个整数 x,y,z,表示点 x 和 y 之间有一条长度为 z 的双向边;

第 N+1 行有一个正整数 M;

接下来 M 行每行是一个事件,事件是以下三种格式之一:

+ x:表示点 x 上出现了异象石;
- x:表示点 x 上的异象石被摧毁;
?:表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。
输出格式
对于每个 ? 事件,输出一个整数表示答案。

样例
样例输入
6
1 2 1
1 3 5
4 1 7
4 5 3
6 4 2
10
+ 3
+ 1
?
+ 6
?
+ 5
?
- 6
- 3
?
样例输出
5
14
17
10
代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<ctime>
#define maxn 100005
#define maxx 18
#define ll long long
using namespace std;
int head[maxn],_next[maxn<<1],to[maxn<<1],w[maxn<<1];
int edge;
void addEdge(int x,int y,int z)
{
    to[++edge]=y,w[edge]=z,_next[edge]=head[x],head[x]=edge;
    to[++edge]=x,w[edge]=z,_next[edge]=head[y],head[y]=edge;
}
int d[maxn];
ll dis[maxn];
queue<int>que;
int n,m,N;
int f[maxn][maxx];
void bfs()
{
    d[1]=1;
    que.push(1);
    while(que.size())
    {
        int u=que.front();que.pop();
        for(int i=head[u];i;i=_next[i])
        {
            int v=to[i];
            if(d[v])continue;
            d[v]=d[u]+1;
            dis[v]=dis[u]+w[i];
            f[v][0]=u;
            for(int i=1;i<=N;i++)
                f[v][i]=f[f[v][i-1]][i-1];
            que.push(v);
        }
    }
}
int lca(int a,int b)
{
    if(d[a]>d[b])swap(a,b);
    for(int i=N;i>=0;i--)
        if(d[a]<=d[f[b][i]])b=f[b][i];
    if(a==b)return a;
    for(int i=N;i>=0;i--)
        if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
    return f[a][0];
}
ll getRoad(int a,int b)
{
    return dis[a]+dis[b]-2*dis[lca(a,b)];
}
int dfn[maxn],ind;
void dfs(int u,int fa)
{
    dfn[u]=++ind;
    for(int i=head[u];i;i=_next[i])
    {
        int v=to[i];
        if(v==fa)continue;
        dfs(v,u);
    }
}

void init()
{
    //memset(head,0,sizeof(head));
    edge=0;
    N=(int)log2(n)+1;
}
struct cmp
{
    bool operator() (const int&a,const int&b)
    {
        return dfn[a]<dfn[b];
    }
};
set<int,cmp>_set;
int main()
{
    //cout<<log2(maxn)<<endl;
    cin>>n;
    init();
    int x,y,z;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        addEdge(x,y,z);
    }
    //clock_t start,finish;
    //start=clock();
    bfs();
    dfs(1,0);
    char s[5];
    cin>>m;
    ll ans=0;
    set<int,cmp>::iterator it,_it;
    while(m--)
    {
        scanf("%s",s);
        if(s[0]=='?')
            printf("%lld\n",ans/2);
        else
        {
            scanf("%d",&x);
            if(s[0]=='+')
            {
                _set.insert(x);
                if(_set.size()>1)
                {
                    it=_set.find(x);
                    _it=_set.end();
                    _it--;
                    int l,r;
                    if(it==_set.begin())l=*(_it);
                    else
                    {
                        it--;l=*it;it++;
                    }
                    it++;
                    if(it==_set.end())r=*(_set.begin());
                    else r=*it;
                    ans-=getRoad(l,r);ans+=getRoad(l,x);ans+=getRoad(x,r);
                }
            }
            else
            {
                if(_set.size()>1)
                {
                    it=_set.find(x);
                    _it=_set.end();
                    _it--;
                    int l,r;
                    if(it==_set.begin())l=*(_it);
                    else
                    {
                        it--;l=*it;it++;
                    }
                    it++;
                    if(it==_set.end())r=*(_set.begin());
                    else r=*it;
                    ans+=getRoad(l,r);ans-=getRoad(l,x);ans-=getRoad(x,r);
                }
                _set.erase(x);
            }
        }
    }
    //finish=clock();
    //printf("%.3lf\n",(finish-start)/CLOCKS_PER_SEC);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值