CH Round #56 异象石(”动态查询树节点之间的路径长度” 时间戳+树上倍增LCA+map/set)

链接:http://contest-hunter.org:83/contest/CH%20Round%20%2356%20-%20%E5%9B%BD%E5%BA%86%E8%8A%82%E6%AC%A2%E4%B9%90%E8%B5%9B/%E5%BC%82%E8%B1%A1%E7%9F%B3

题意:给出一n节点的树状结构,m此操作,每次操作有3种选择,第一种:某块石头出现、第二种:某块石头消失、第三种:查询使当前所有异象石链接的路线的权值和。

分析:

      通过“简单”的找规律,我们发现,如果将某一时刻已经出现的所有异象石按照时间戳排成一个,叠加相邻两点的距离和最后便可以得到所求答案的两倍。(什么时候我自己能推出来呢)。

      按时间戳排序我们可以先dfs求时间戳(顺便能求出树上倍增求LCA所要用的深度,f数组等量),然后全部塞到某STL中即可。然后怎么维护这个动态序列的权值和呢。新出现一个点的时候,我们应该找到该点在序列中应当所处的位置,然后删除前前后两个点之间的距离,新增加该点与前一个点和该点与后一个点的距离(树中两点之间的距离可以用LCA的方法)。删除节点做相反处理即可。(这里需要注意的是迭代器的使用,越界、怎样访问前后节点等问题均需要注意)。查询时直接输出全局变量ans/2即可。

      最后,刚开始看到这题,自己首先想到的是最小生成树,但是kruscal mlogm和prime的mlogn/n^2加上10^5次查询,都会超时,按照题解算法,外层查询10^5,内部耗时主要在求LCA上(map的查询logn,链接:C++ STL中常见容器的时间复杂度),树上倍增法求LCA时间复杂度logN,显然比最小生成树好很多。

 

WA了一上午,原因如下:

①红黑树操作不熟,尤其是迭代器的几个易错点;

②数组开小了(题目有问题);

③ll问题(10^9做加法怎么还用int呢);

dfs和bfs可合并,去找oj上的代码,这里先贴上没合并的。

 

AC代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include<ctime>
#define ll long long
using namespace std;
const int N=100010;
const int M=1000100;
int head[N];
int edge[M],ver[M];
int tot;
int v[N];
int Next[M];
void add(int x,int y,int z)
{
     edge[++tot]=z;
     ver[tot]=y;
     Next[tot]=head[x];
     head[x]=tot;
}
int dfn[N];
int kk;
void dfs(int x)//求时间戳,可以在这里求f数组和树节点深度,下面的bfs就可以省去了
{
    dfn[x]=++kk;
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(dfn[y])continue;
        dfs(y);
    }
}
int f[N][20],d[N];
ll dist[N];
int t;
queue <int>q;
void bfs()//树上倍增求LCA预处理
{
    q.push(1);
    d[1]=1;

    while(q.size())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if(d[y])continue;
            d[y]=d[x]+1;
            dist[y]=dist[x]+edge[i];
            f[y][0]=x;
            for(int j=1;j<=t;++j)
            {
                f[y][j]=f[f[y][j-1]][j-1];
            }
            q.push(y);
        }
    }
}
int lca(int x,int y)//倍增求LCA
{
    if(d[x]>d[y])swap(x,y);
    for(int i=t;i>=0;--i)
        if(d[f[y][i]]>=d[x])y=f[y][i];

    if(x==y)return x;
    for(int i=t;i>=0;--i)
        if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
map<int,int >mm;
map<int,int>::iterator it;
ll path(map<int,int>::iterator it1,map<int,int>::iterator it2)//LCA法计算树中两节点之间距离
{
    ll ans= dist[it1->second]+dist[it2->second];
    //cout<<ans<<"''   "<<it1->second<<" "<<it2->second<<endl;
    ans-=2*dist[lca(it1->second,it2->second)];
    //cout<<ans<<"''"<<endl;
    return ans;
}
int main()
{
    //freopen("input.txt","r",stdin);
    int n;
    while(cin>>n)
    {
        tot=0;
        kk=0;
        mm.clear();
        memset(Next,0,sizeof(Next));
        memset(d,0,sizeof(d));
        memset(dist,0,sizeof(dist));
        memset(head,0,sizeof(head));
        memset(edge,0,sizeof(edge));
        memset(dfn,0,sizeof(dfn));
        memset(ver,0,sizeof(ver));
        for(int i=1;i<n;++i)
        {
            int x,y,z;
            cin>>x>>y>>z;
            add(x,y,z);
            add(y,x,z);
        }
        t=(int)(log(n)/log(2))+1;
        bfs();
        dfs(1);
        int m;
        cin>>m;
        ll ans=0;



        while(m--)
        {
            char ch;
            int x;
            cin>>ch;
            if(ch=='+')//这一部分写的很乱很麻烦,已经没有一点条理了。
            {
                cin>>x;
                mm.insert(make_pair(dfn[x],x));
                it=mm.find(dfn[x]);
                map<int,int>::iterator it1;
                map<int,int>::iterator it2;

                if(mm.size()==1)
                {
                    //cout<<"..."<<endl;
                    continue;
                }
                else if(mm.size()==2)
                {
                    ans+=path(mm.begin(),--mm.end())*2;
                    continue;
                }
                else if(it==mm.begin())
                {
                    it1=--mm.end();
                    it2=++it;
                    it--;
                    ans+=path(mm.begin(),++mm.begin())+path(mm.begin(),--mm.end());
                    ans-=path(it2,it1);

                }
                else if(it==--mm.end())
                {
                    it2=mm.begin();
                    it1=--it;
                    it++;//再加回来
                    ans+=path(it1,it)+path(it,it2);
                    ans-=path(it1,it2);
                }
                else
                {
                    it2=++it;
                    --it;
                    it1=--it;
                    it++;
                    ans-=path(it1,it2);
                    ans+=path(it,it1)+path(it,it2);

                }


                //cout<<path(it,it1)<<"  "<<path(it,it2)<<endl;

            }
            else if(ch=='-')
            {
                cin>>x;
                it=mm.find(dfn[x]);
                map<int,int>::iterator it1;
                map<int,int>::iterator it2;

                if(mm.size()==1)
                {
                    mm.erase(mm.begin());
                    continue;
                }
                else if(mm.size()==2)
                {
                    ans-=path(mm.begin(),--mm.end())*2;
                    mm.erase(it);
                    continue;
                }
                else if(it==mm.begin())
                {
                    it1=--mm.end();
                    it2=++it;
                    it--;
                    ans-=path(mm.begin(),++mm.begin())+path(mm.begin(),--mm.end());
                    ans+=path(it1,it2);

                }
                else if(it==--mm.end())
                {
                    it2=mm.begin();
                    it1=--it;
                    it++;
                    ans-=path(it1,it)+path(it,it2);
                    ans+=path(it1,it2);
                }
                else
                {
                    it2=++it;
                    --it;
                    it1=--it;
                    it++;
                    ans+=path(it1,it2);
                    ans-=path(it,it1)+path(it,it2);

                }

                mm.erase(it);
            }
            else
            {
                cout<<ans/2<<endl;
            }
        }


    }
    return 0;
}

 

The end;

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值