Loading

BZOJ1912 异象石(LCA/DFS序/set)

Adera是Microsoft应用商店中的一款解谜游戏。

异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图。

这张地图上有N个点,有N-1条双向边把它们连通起来。

起初地图上没有任何异象石,在接下来的M个时刻中,每个时刻会发生以下三种类型的事件之一:

  1. 地图的某个点上出现了异象石(已经出现的不会再次出现);
  2. 地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);
  3. 向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。

请你作为玩家回答这些问题。

输入格式

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

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

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

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

”+ x” 表示点x上出现了异象石

”- x” 表示点x上的异象石被摧毁

”?” 表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。

输出格式

对于每个 ? 事件,输出一个整数表示答案。

数据范围

1N,M1051≤N,M≤105,
1x,yN1≤x,y≤N,
xyx≠y,
1z1091≤z≤109

输入样例:

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
蓝书上的例题,主要说一下注意的地方。
1.dist数组和ans都要开long long,不然见祖宗。
2.既然要求时间戳,不如直接dfs预处理f数组(不要按照蓝书板子用bfs求了)。
3.注意,set里面的s.end()返回的是指向最后一个元素下一个位置的迭代器!这个位置存放的是set的size!同时迭代器只能自增自减,要么写函数处理要么多设置几个迭代器。
4.注意下魔鬼输入。看到有博客这么写觉得很巧妙:
char c = getchar();
while (c != '+' && c != '-' && c != '?') c = getchar();
#include <bits/stdc++.h>
#define N 100005
using namespace std;
int t,n,m,tot=0,head[N],edge[N*2],Next[N*2],ver[N*2],f[N][25],d[N];
long long dist[N];
int cnt=0;//时间戳 
long long ans=0;
struct node
{
    int x;
    int t;//时间戳 
}nod[N];
struct cmp 
{
    bool operator() (const node& a, const node& b) const{
        return a.t < b.t;
    }
};
set<node,cmp>s;
void add(int x,int y,int z)
{
    ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
void dfs(int x,int pre)//因为要记录时间戳,所以用dfs 
{
    int i;
    nod[x].t=(++cnt);
    for(i=head[x];i;i=Next[i])
    {
        int y=ver[i],z=edge[i];
        if(y==pre)continue;
        d[y]=d[x]+1;
        dist[y]=dist[x]+(long long)z;
        f[y][0]=x;
        int j;
        for(j=1;j<=t;j++)f[y][j]=f[f[y][j-1]][j-1];
        dfs(y,x);
    }
 } 
int lca(int x,int y)
{
    if(d[x]>d[y])swap(x,y);
    int i;
    for(i=t;i>=0;i--)
    {
        if(d[f[y][i]]>=d[x])y=f[y][i];
    }
    if(x==y)return x;
    for(i=t;i>=0;i--)
        if(f[y][i]!=f[x][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
long long path(int x,int y)
{
    int anc=lca(x,y);
    return dist[x]+dist[y]-2*dist[anc];
 } 
int main()
{
    int i;
    cin>>n;
    for(i=1;i<=n;i++)nod[i].x=i;
    d[1]=1,dist[1]=0;
    t=(int)(log(n)/log(2))+1;
    for(i=1;i<=n-1;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    dfs(1,0);
    scanf("%d",&m);
    for(i=1;i<=m;i++)
    {
        char c = getchar();
        while (c != '+' && c != '-' && c != '?') c = getchar();
        int x,l,r;
        set<node,cmp> :: iterator it1,it2;
        if(c=='+')
        {
            scanf("%d",&x);
            s.insert(nod[x]);
            it1=it2=s.find(nod[x]);
            if(it1==s.begin())l=(--s.end())->x;//? 保证首尾相接 
            else l=(--it1)->x;    //只能自增自减        
            if(++it2==s.end())r=(s.begin())->x;//?
            else r=(it2)->x;
            ans+=path(l,x)+path(x,r)-path(l,r);
            //cout<<l<<' '<<r<<endl;

        }
        else if(c=='-')
        {
            scanf("%d",&x);
            it1=it2=s.find(nod[x]);
            set<node,cmp> :: iterator temp=it1;//自增自减后还能找得到x的位置以便删除 
            if(it1==s.begin())l=(--s.end())->x;//?
            else l=(--it1)->x;
            if(++it2==s.end())r=(s.begin())->x;//?
            else r=(it2)->x;
            ans+=path(l,r)-path(l,x)-path(x,r);
            s.erase(temp);//别忘记删除 
        }
        else
        {
            cout<<ans/2<<endl;
        }
    }
//9
//1 2 1
//1 3 5
//1 4 2
//2 5 4
//2 6 4
//4 7 7
//4 8 5
//7 9 3
}

 

 
posted @ 2020-04-10 17:42  脂环  阅读(238)  评论(0编辑  收藏  举报