zoukankan      html  css  js  c++  java
  • 【洛谷P4949】最短距离【树剖】【基环树】

    题目大意:

    题目链接:https://www.luogu.org/problem/P4949
    给出一个NN个点NN条边的无向连通图。
    你需要支持两种操作:

    1. 修改 第xx条边的长度为yy
    2. 查询 点xx到点yy的最短距离

    共有MM次操作。


    思路:

    第一道没有看题解写出来的黑题祭。果然还是tcltcl
    这道题给出的图是一棵基环树,先考虑如果是一棵树应该如何处理。
    显然一棵树的时候就是树链剖分的一道裸题。线段树单点修改和查询区间和即可。
    那么在这棵树上加上一条边(u,v)(u,v),原来(x,y)(x,y)的答案可能会有以下改变:

    1. 依然是没有加边前的答案dis(x,y)dis(x,y)
    2. dis(x,u)+dis(u,v)+dis(y,v)dis(x,u)+dis(u,v)+dis(y,v)
    3. dis(x,v)+dis(u,v)+dis(y,u)dis(x,v)+dis(u,v)+dis(y,u)

    而我们对于一条路径(p,q)(p,q),只需要求出p,qp,qlcalca,然后路径长度就分成(p,lca)+(q,lca)(p,lca)+(q,lca)
    所以先找到基环树上的环,在环上随便删除一条边,然后每次询问就分别求出(x,y),(x,u),(x,v),(y,u),(y,v)(x,y),(x,u),(x,v),(y,u),(y,v)的长度,然后在上述三条式子中取最小值即可。
    对了最开始肯定套路性的把边权转换为点权再做树剖。
    时间复杂度O(mlog2n)O(mlog^2 n),常数巨大,因为每次要求5组点对的距离,而每组点对又要拆分成两条路径并求一个lcalca。。。
    肯定没有黑题难度啊。


    代码:

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=100010;
    int head[N],son[N],fa[N],size[N],dep[N],top[N],id[N],rk[N],in[N],U[N],V[N],Dis[N],val[N];
    int n,m,tot,flag,opt,x,y,ans1,ans2,ans3,LCA;
    queue<int> q;
    
    struct edge
    {
    	int next,to,dis;
    }e[N*2];
    
    void add(int from,int to,int dis)
    {
    	e[++tot].to=to;
    	e[tot].dis=dis;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    struct Treenode
    {
    	int l,r,sum;
    };
    
    struct Tree
    {
    	Treenode tree[N*4];
    	
    	int pushup(int x)
    	{
    		tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
    	}
    	
    	void build(int x,int l,int r)
    	{
    		tree[x].l=l; tree[x].r=r;
    		if (l==r)
    		{
    			tree[x].sum=val[rk[l]];
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(x*2,l,mid); build(x*2+1,mid+1,r);
    		pushup(x);
    	}
    	
    	void update(int x,int k,int v)
    	{
    		if (tree[x].l==k && tree[x].r==k)
    		{
    			tree[x].sum=v;
    			return;
    		}
    		int mid=(tree[x].l+tree[x].r)>>1;
    		if (k<=mid) update(x*2,k,v);
    			else update(x*2+1,k,v);
    		pushup(x);
    	}
    	
    	int ask(int x,int l,int r)
    	{
    		if (tree[x].l==l && tree[x].r==r)
    			return tree[x].sum;
    		int mid=(tree[x].l+tree[x].r)>>1;
    		if (r<=mid) return ask(x*2,l,r);
    		if (l>mid) return ask(x*2+1,l,r);
    		return ask(x*2,l,mid)+ask(x*2+1,mid+1,r); 
    	}
    }Tree;
    
    void dfs1(int x,int f)
    {
    	dep[x]=dep[f]+1; size[x]=1; fa[x]=f;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=f)
    		{
    			dfs1(v,x);
    			size[x]+=size[v];
    			if (size[v]>size[son[x]]) son[x]=v;
    		}
    	}
    }
    
    void dfs2(int x,int tp)
    {
    	top[x]=tp; id[x]=++tot; rk[tot]=x;
    	for (int i=head[x];~i;i=e[i].next)
    		if (e[i].to==son[x]) dfs2(e[i].to,tp);
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=son[x] && v!=fa[x]) dfs2(v,v);
    	}
    }
    
    int lca(int x,int y)
    {
    	while (top[x]!=top[y])
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);
    		x=fa[top[x]];
    	}
    	if (dep[x]<dep[y]) return x;
    		else return y;
    }
    
    int ask(int x,int y)
    {
    	int ans=0;
    	while (top[x]!=top[y])
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);
    		ans+=Tree.ask(1,id[top[x]],id[x]);
    		x=fa[top[x]];
    	}
    	if (dep[x]>dep[y]) swap(x,y);
    	return ans+Tree.ask(1,id[x],id[y]);
    }
    
    int ask_(int x,int y)  //拆分成两条路径
    {
    	LCA=lca(x,y);
    	return ask(x,LCA)+ask(y,LCA)-Tree.ask(1,id[LCA],id[LCA])*2;
    }
    
    void topsort()  //基环树拓扑排序求环
    {
    	for (int i=1;i<=n;i++)
    		if (in[i]==1) q.push(i);
    	while (q.size())
    	{
    		int u=q.front();
    		q.pop();
    		for (int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			in[v]--;
    			if (in[v]==1) q.push(v);
    		}
    	}
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d%d%d",&U[i],&V[i],&Dis[i]);
    		add(U[i],V[i],Dis[i]); add(V[i],U[i],Dis[i]);
    		in[U[i]]++; in[V[i]]++;
    	}
    	topsort();
    	tot=0;
    	memset(head,-1,sizeof(head));
    	for (int i=1;i<=n;i++)
    		if (in[U[i]]>1 && in[V[i]]>1 && !flag)
    			flag=i;  //记录环上的任意一条边
    		else
    			add(U[i],V[i],Dis[i]),add(V[i],U[i],Dis[i]);
    	tot=0;
    	dfs1(1,0); dfs2(1,1);
    	for (int i=1;i<=n;i++)
    		if (dep[U[i]]>dep[V[i]])
    			val[U[i]]=Dis[i];
    		else
    			val[V[i]]=Dis[i];
    	Tree.build(1,1,n);
    	while (m--)
    	{
    		scanf("%d%d%d",&opt,&x,&y);
    		if (opt==1)
    		{
    			if (x==flag) Dis[x]=y;
    			else if (dep[U[x]]>dep[V[x]]) Tree.update(1,id[U[x]],y);
    				else Tree.update(1,id[V[x]],y);
    		}
    		else
    		{
    			ans1=ask_(x,y);
    			ans2=ask_(x,U[flag])+ask_(y,V[flag])+Dis[flag];
    			ans3=ask_(x,V[flag])+ask_(y,U[flag])+Dis[flag];
    			printf("%d
    ",min(ans1,min(ans2,ans3)));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Django-orm-自关联
    Android中像素相关
    Android导入项目工程时报错“ Invalid project description”
    汇编之64位操作系统安装debug.exe
    mygui初探(二)皮肤编辑器
    mygui初探(一)基础
    一种破解静态链接库(.lib)的简单方法
    工作这两年的经验与教训
    3D单机游戏《天鹰教》源码发布(二)
    HUSTOJ的Windows版评判内核(二)
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998016.html
Copyright © 2011-2022 走看看