题意:给出一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;