zoukankan      html  css  js  c++  java
  • [HDU3686] Traffic Real Time Query System

    题目

    原题链接

    解说

    思路一眼就能看出来:圆方树+(LCA)。但是真正写起来是真的恶心,好多的细节,调了一晚上……
    圆方树和找(LCA)没什么可说的,板子往上一套即可。主要要说的是求两点间有几个必须经过的点。
    首先很显然两点间必须经过的点数量即为圆方树上两点间路径上的圆点数量,那么我们可以先(DFS)初始化出来每个点的深度,然后进行计算。
    假设这两个点为(A)(B),它们的公共祖先为(LCA),那么可以分为两种情况:(LCA)为圆点或方点,我们分别计算。
    假设(LCA)为方点:
    可见任意一个点到(LCA)之间的圆点数为((deep[A|B]-deep[LCA]-1)/2)(-1)是因为要去除(LCA)这个方点,除二是因为圆方树圆方点相间。
    这样的话把两点到(LCA)之间的圆点数相加即可得到两点间圆点数为:((deep[A]+deep[B]-2*deep[LCA])/2-1)
    假设(LCA)为圆点:
    任意一个点到(LCA)之间的圆点数为((deep[A|B]-deep[LCA])/2),因为(LCA)是圆点所以不用(-1)(当然,算出来的圆点数是包含(LCA)的)。
    在把两个点相加的时候由于两边都包含了(LCA),有重复计算,所以再(-1):
    ((deep[A]-deep[LCA])/2+(deep[B]-deep[LCA])/2-1=(deep[A]+deep[B]-2*deep[LCA])/2-1)
    非常棒!两边的式子是一样的,都不用再判断了!
    最后注意题目中说的是两条边之间的距离而非两个点之间的距离(坑死我了呜呜呜……),所以我们要枚举每个边的起点和终点计算出来经过圆点数量的最大值。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define v e[i].to
    const int maxn=10000+3,maxe=100000+3;
    int n,m,tot,head[maxn],h[2*maxn],js,dfs_clock,deep[2*maxn],father[2*maxn];
    int bcc_cnt,sta[maxn],top,dfn[maxn],low[maxn];
    bool vis[2*maxn];
    //涉及圆方树连接的记得开2倍
    struct edge{
    	int from,to,next;
    }e[2*maxe],ed[2*maxe];
    void Add(int a,int b){//原图加边
    	e[++tot].from=a;
    	e[tot].to=b;
    	e[tot].next=head[a];
    	head[a]=tot;
    }
    void Add2(int a,int b){//圆方树加边
    	ed[++js].from=a;
    	ed[js].to=b;
    	ed[js].next=h[a];
    	h[a]=js;
    }
    void tarjan(int u){//求点双&构建圆方树
    	low[u]=dfn[u]=++dfs_clock;
    	sta[++top]=u;
    	for(int i=head[u];i;i=e[i].next){
    		if(dfn[v]) low[u]=min(low[u],dfn[v]);
    		else{
    			tarjan(v);
    			low[u]=min(low[v],low[u]);
    			if(low[v]==dfn[u]){
    				++bcc_cnt;
    				int temp;
    				do{
    					temp=sta[top];
    					top--;
    					Add2(n+bcc_cnt,temp);
    					Add2(temp,n+bcc_cnt);
    				}while(temp!=v);
    				Add2(u,n+bcc_cnt);
    				Add2(n+bcc_cnt,u);
    			}
    		}
    	}
    }
    void init(){
    	tot=0; js=0; top=0; dfs_clock=0; bcc_cnt=0;
    	memset(head,0,sizeof(head));
    	memset(e,0,sizeof(e));
    	memset(h,0,sizeof(h));
    	memset(ed,0,sizeof(ed));
    	memset(sta,0,sizeof(sta));
    	memset(dfn,0,sizeof(dfn));
    	memset(low,0,sizeof(low));
    	memset(deep,0,sizeof(deep));
    	memset(vis,0,sizeof(vis));
    	memset(father,0,sizeof(father));
    	memset(sta,0,sizeof(sta));
    }
    void dfs(int x){//预处理深度和父节点
    	vis[x]=1;
    	for(int i=h[x];i;i=ed[i].next){
    		int now=ed[i].to;
    		if(vis[now]) continue;
    		deep[now]=deep[x]+1;
    		father[now]=x;
    		dfs(now);
    	}
    }
    int find(int a,int b){//求LCA,别的地方太麻烦了这里懒得用倍增了……反正也超不了时
    	if(deep[a]<deep[b]) swap(a,b);
    	while(deep[a]>deep[b]) a=father[a];
    	while(a!=b){
    		a=father[a];
    		b=father[b];
    	}
    	return a;
    }
    int len(int a,int b){
    	int lca=find(a,b);
    	return (deep[a]+deep[b]-2*deep[lca])/2-1;
    }
    int main(){
    	while(1){
    		scanf("%d%d",&n,&m);
    		if(n==0&&m==0) return 0;
    		init();
    		for(int i=1;i<=m;i++){
    			int x,y;
    			scanf("%d%d",&x,&y);
    			Add(x,y);
    			Add(y,x);
    		}
    		for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    		for(int i=1;i<=n+bcc_cnt;i++) if(!vis[i]) dfs(i);
    		int t;
    		scanf("%d",&t);
    		while(t--){
    			int x,y;
    			scanf("%d%d",&x,&y);
    			int a=e[x*2].from,b=e[x*2].to,c=e[y*2].from,d=e[y*2].to; //枚举节点
    			printf("%d
    ",max(max(len(b,d),len(a,d)),max(len(b,c),len(a,c))));
    		}
    	}
    }
    

    幸甚至哉,歌以咏志。

  • 相关阅读:
    Java 分布式系统 实现session共享
    MySQL 大数据量使用limit分页,随着页码的增大,查询效率越低下。
    Linux下安装Zookeeper
    Mysql Window 下安装
    Spring Boot 教程demo
    全文搜索引擎 Elasticsearch (三)logstash-input-jdbc同步数据 到elasticsearch
    全文搜索引擎 Elasticsearch (二) 使用场景
    67.基于nested object实现博客与评论嵌套关系
    66.基于共享锁和排他锁实现悲观锁并发控制
    65.基于document锁实现悲观锁并发控制
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/12840829.html
Copyright © 2011-2022 走看看