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;
    }
    
  • 相关阅读:
    VS 2008潜在强大的功能:提取EXE文件中的ICO等资源
    园友们注意:淘宝网上QQ会员 4钻 3元 等都为骗子行为
    Comet Async Process Request Handler
    WCF(Sender) to MSMQ to WCF(Receiver)
    ASP.NET Web Form GridView DetailsView Query Edit
    WCF NetTcp AsyncQueue Service
    Xml CDATA 序列化
    Sync Invoke Remoting Async Invoke
    .Net 4.0 Remoting ConcurrentQueue
    Socket Async Receive Data to LinkedList Buffer (telnet proxy server)
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998016.html
Copyright © 2011-2022 走看看