zoukankan      html  css  js  c++  java
  • 【BZOJ】3991: [SDOI2015]寻宝游戏

    题意

    给一个(n)个点带边权的树。有(m)次操作,每一次操作一个点(x),如果(x)已经出现,则(x)消失。否则(x)出现。每一操作后,询问从某个点开始走,直到经过所有出现的点,最后再回到开始的那个点的最短路程。

    分析

    首先容易知道我们选任意一个在某两点路径上的点作为起点都能得到最优解(包括端点)。我们只需要考虑走的顺序。

    题解

    由于按照dfs序的走法是最短的,因此我们按dfs序维护一下前前后后的距离和即可。如何证明?好像并不会...

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=100005;
    int n, ihead[N], FF[N], dep[N], cnt, a[N], fa[N][17], m;
    ll d[N];
    struct E {
    	int next, to, w;
    }e[N<<1];
    struct dat {
    	int FF, id;
    	ll dis;
    	bool operator < (const dat &a) const {
    		return FF<a.FF;
    	}
    };
    set<dat> s;
    void add(int x, int y, int w) {
    	e[++cnt]=(E){ihead[x], y, w}; ihead[x]=cnt;
    	e[++cnt]=(E){ihead[y], x, w}; ihead[y]=cnt;
    }
    void dfs(int x, int f=0) {
    	static int fid=0;
    	FF[x]=++fid;
    	for(int i=1; i<=16; ++i) {
    		fa[x][i]=fa[fa[x][i-1]][i-1];
    	}
    	for(int i=ihead[x]; i; i=e[i].next) {
    		int y=e[i].to;
    		if(y==f) {
    			continue;
    		}
    		fa[y][0]=x;
    		dep[y]=dep[x]+1;
    		d[y]=d[x]+e[i].w;
    		dfs(y, x);
    	}
    }
    int LCA(int x, int y) {
    	if(dep[x]<dep[y]) {
    		swap(x, y);
    	}
    	int d=dep[x]-dep[y];
    	for(int i=16; i>=0; --i) if((d>>i)&1) x=fa[x][i];
    	if(x==y) return x;
    	for(int i=16; i>=0; --i) if(fa[x][i]!=fa[y][i]) x=fa[x][i], y=fa[y][i];
    	return fa[x][0];
    }
    ll getdis(int x, int y) {
    	int lca=LCA(x, y);
    	return d[x]+d[y]-(d[lca]<<1);
    }
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i=1; i<n; ++i) {
    		int x, y, w;
    		scanf("%d%d%d", &x, &y, &w);
    		add(x, y, w);
    	}
    	dfs((n+1)>>1);
    	ll ans=0;
    	while(m--) {
    		int p;
    		scanf("%d", &p);
    		dat t;
    		if(a[p]) {
    			set<dat>::iterator it=s.lower_bound((dat){FF[p], 0, 0}), itp=s.end(), itb=s.end();
    			itb=it;
    			++itb;
    			ans-=it->dis;
    			if(it!=s.begin()) {
    				itp=it;
    				--itp;
    			}
    			if(itb!=s.end()) {
    				ans-=itb->dis;
    				if(itp==s.end()) {
    					t=*itb;
    					t.dis=0;
    					s.erase(itb);
    					s.insert(t);
    				}
    				else {
    					t=*itb;
    					t.dis=getdis(itb->id, itp->id);
    					ans+=t.dis;
    					s.erase(itb);
    					s.insert(t);
    				}
    			}
    			s.erase(it);
    			a[p]=0;
    		}
    		else {
    			set<dat>::iterator it=s.lower_bound((dat){FF[p], 0, 0}), itp=s.end();
    			if(it!=s.begin()) {
    				itp=it;
    				--itp;
    			}
    			if(it!=s.end()) {
    				if(itp!=s.end()) {
    					ll dis=getdis(p, itp->id);
    					s.insert((dat){FF[p], p, dis});
    					ans+=dis;
    				}
    				else {
    					s.insert((dat){FF[p], p, 0});
    				}
    				t=*it;
    				ans-=t.dis;
    				t.dis=getdis(p, it->id); 
    				ans+=t.dis;
    				s.erase(it);
    				s.insert(t);
    			}
    			else {
    				if(itp==s.end()) {
    					s.insert((dat){FF[p], p, 0});
    				}
    				else {
    					ll dis=getdis(p, itp->id);
    					s.insert((dat){FF[p], p, dis});
    					ans+=dis;
    				}
    			}
    			a[p]=1;
    		}
    		ll temp=0;
    		if(s.size()>=2) {
    			set<dat>::iterator it=s.end();
    			--it;
    			temp=getdis(s.begin()->id, it->id);
    		}
    		printf("%lld
    ", ans+temp);
    	}
    	return 0;
    }
  • 相关阅读:
    【特种兵系列】String中的==和equals()
    windows7 x64下maven安装和配置
    微信开发之本地接口调试(非80端口,无需上传服务器服务器访问)
    Java微信公众平台接口封装源码分享
    jdk安装
    用户权限管理
    Linux常用命令(一)
    Xshell显示中文乱码问题
    伪装虽易测试不易之微信浏览器
    每次新建项目出现appcompat_v7 解决方法
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4986317.html
Copyright © 2011-2022 走看看