【BZOJ3991】寻宝游戏(SDOI2015)-贪心+DFS序+set
测试地址:寻宝游戏
做法:本题需要用到DFS+set。
首先需要看出,无论从哪个点出发结果都相同。然后就是要找一种能得到最优解的走法,显然走每条边次是最优的,那怎么样构造出走法呢?其实只要按照这些点的DFS序顺序走下去,最后走回第一个点即可。因为对于涉及到的每条边,只下去一次再上来一次,所以这个肯定最优。那么我们每次插入或删除一个点时,实际上只影响到它在DFS序中相邻的两个点之间的贡献,那么就用新的贡献替换旧的贡献,因为要求两点间的距离,用倍增求出即可。至于找到在当前点集中DFS序相邻的点,可以用set的lower_bound和upper_bound函数解决。
学了这么久C++,居然现在才会用set,除了常数大一点还真是好用啊……
以下是本人代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,first[100010]={0},tot=0,fa[100010][21],dep[100010];
int pos[100010],r[100010],tim=0;
ll dis[100010];
set<int> S;
set<int>::iterator it=S.begin();
struct edge {int v,next;ll w;} e[200010];
bool vis[100010]={0};
void insert(int a,int b,ll w)
{
e[++tot].v=b,e[tot].w=w,e[tot].next=first[a],first[a]=tot;
}
void dfs(int v)
{
pos[v]=++tim;
r[tim]=v;
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa[v][0])
{
dep[e[i].v]=dep[v]+1;
fa[e[i].v][0]=v;
dis[e[i].v]=dis[v]+e[i].w;
dfs(e[i].v);
}
}
void findlr(int x,int &l,int &r)
{
it=S.lower_bound(x);
if (it==S.begin()) it=S.end(),it--;
else it--;
l=*it;
it=S.upper_bound(x);
if (it==S.end()) it=S.begin();
r=*it;
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if (x==y) return x;
for(int i=20;i>=0;i--)
if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y;
ll z;
scanf("%d%d%lld",&x,&y,&z);
insert(x,y,z),insert(y,x,z);
}
fa[1][0]=dep[1]=0;dep[0]=-1;
dfs(1);
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
ll ans=0;
for(int i=1;i<=m;i++)
{
int x,px,py,g;
scanf("%d",&x);
if (!vis[x])
{
vis[x]=1;
S.insert(pos[x]);
findlr(pos[x],px,py);
ans-=dis[r[px]]+dis[r[py]]-(dis[lca(r[px],r[py])]<<1);
ans+=dis[r[px]]+dis[x]-(dis[lca(r[px],x)]<<1);
ans+=dis[r[py]]+dis[x]-(dis[lca(r[py],x)]<<1);
}
else
{
vis[x]=0;
findlr(pos[x],px,py);
ans+=dis[r[px]]+dis[r[py]]-(dis[lca(r[px],r[py])]<<1);
ans-=dis[r[px]]+dis[x]-(dis[lca(r[px],x)]<<1);
ans-=dis[r[py]]+dis[x]-(dis[lca(r[py],x)]<<1);
S.erase(pos[x]);
}
printf("%lld\n",ans);
}
return 0;
}