• [SDOI2015]寻宝游戏/异象石(LCA)


    题意

    给一颗树和一些操作,每次操作修改一个点的标记(0/1),求从一个标记点出发走过所有标记点最后回到起点的最短路径长度

    思路

    由于最后要回到起点,整条路径构成了一个环,所以起点无所谓;一个点向离它近的点走,这在树上表示为一个点向(dfs)序离它近的点走;所以将所有标记点按照dfs序排序,路径即为(1)$k$顺次连接成环,删除或加入一个点就考虑这个点与前驱后继这三点的关系即可,前驱后继可用$set$维护(也可以手写平衡树~)

    Code

    #include<bits/stdc++.h>
    #define N 100005
    #define IT set< pair<int,int> >::iterator
    using namespace std;
    typedef long long ll;
    int n,m,f[N][18],dep[N],dfn[N],c;
    ll ans=0,dis[N];
    set< pair<int,int> > s;
    bool vis[N];
    struct Edge
    {
    	int next,to;
    	ll dis;
    }edge[N<<1];int head[N],cnt=1;
    void add_edge(int from,int to,ll dis)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	edge[cnt].dis=dis;
    	head[from]=cnt;
    }
    template <class T>
    void read(T &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
    }
    void dfs(int rt,int fa)
    {
    	dfn[rt]=++c;
    	dep[rt]=dep[fa]+1;
    	for(int i=head[rt];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(v==fa) continue;
    		dis[v]=dis[rt]+edge[i].dis;
    		f[v][0]=rt;
    		for(int j=1;j<18;++j) f[v][j]=f[f[v][j-1]][j-1];
    		dfs(v,rt);
    	}
    }
    int lca(int x,int y)
    {
    	if(dep[x]<dep[y]) swap(x,y);
    	for(int i=17;i>=0;--i) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    	if(x==y) return x;
    	for(int i=17;i>=0;--i) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    ll d(int x,int y) {return dis[x]+dis[y]-2LL*dis[lca(x,y)];}
    int main()
    {
    	read(n);read(m);
    	for(int i=1;i<n;++i)
    	{
    		int x,y; ll z;
    		read(x);read(y);read(z);
    		add_edge(x,y,z);
    		add_edge(y,x,z);
    	}
    	dfs(1,0);
    	while(m--)
    	{
    		int x; read(x);
    		if(vis[x])//删除x
    		{
    			vis[x]=0;
    			if((int)s.size()==1)
    			{
    				ans=0;
    				s.clear();
    				goto u;
    			} 
    			IT pre=s.lower_bound(make_pair(dfn[x],x));
    			IT nxt=s.upper_bound(make_pair(dfn[x],x));
    			if(pre==s.begin()) pre=s.end();
    			if(nxt==s.end()) nxt=s.begin();
    			--pre;
    			ans-=d(x,pre->second);
    			ans-=d(x,nxt->second);
    			ans+=d(pre->second,nxt->second);
    			s.erase(make_pair(dfn[x],x));
    		}
    		else//加入x 
    		{
    			vis[x]=1;
    			s.insert(make_pair(dfn[x],x));
    			if((int)s.size()==1) goto u;
    			IT pre=s.lower_bound(make_pair(dfn[x],x));
    			IT nxt=s.upper_bound(make_pair(dfn[x],x));
    			if(pre==s.begin()) pre=s.end();
    			if(nxt==s.end()) nxt=s.begin();
    			--pre;
    			ans+=d(x,pre->second);
    			ans+=d(x,nxt->second);
    			ans-=d(pre->second,nxt->second);
    		}
    		u:; 
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Win10下 Docker Flask实例
    4.1 线性映射的概念
    桥梁的基本组成和分类
    Qt5字符串编码转换学习
    在右键菜单中添加用Jupyter Notebook打开
    左右手(直角)坐标系叉乘计算公式
    __new__方法与单键实例
    向量组的秩
    从线性组合的角度理解三维运算
    Hexo使用小结
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11632282.html
Copyright © 2020-2023  润新知