• HDU 5915 The Fastest Runner Ms. Zhang (CCPC2016 长春 E题,分类讨论 + 求字典序最小的直径 + 数据结构寻找最小值)


    题目链接  CCPC2016 Changchun Problem E

    题意  给定一个$n$个点$n$条边的无向图,现在从某一点$s$出发,每个点都经过一遍,最后在$t$点停止,经过的边数为$l$

        求字典序最小的三元组$(l, s, t)$

     

    设环的长度为$c$,

    当$s$和$t$在同一棵子树上的时候,$s$到$t$的路径上的边和环上的边只要走一次,其他边都要走两次,那么答案为$2n - c - len$

    $len$为$s$到$t$的路径的长度;

     

    当$s$和$t$不在同一棵子树上的时候,设$s$走到环上的第一个点为$u$,$t$走到环的第一个点为$v$。

    那么这个时候考虑每条边走过的次数。

    对于不在环上的边,从$s$到$u$,从$v$到$t$的路径只要走一次,其余的边都要走两次。

    对于在环上的边,从$u$到$v$的路径只要走一次,其余的部分都要走两次,但是有一条边不用经过(一次都不用走)

    那么答案为$2n - 2 - len(s, u) - len(u, v) - len(v, t)$;

     

    对于第一种情况,我们对环上每棵树都求一条字典序最小的直径然后更新答案即可。

    对于第二种情况,我们需要最大化$len(s, u) + len(u, v) + len(v, t)$的值。

    考虑每个环上的点,显然$s$和$t$的最佳选择都是从这个点往里走可以走到的最深的点。

    设环上的点依次排列为:$a_{1}, a_{2}, a_{3}, ......, a_{cnt}$。

    设每个环上的点往子树方向走能走到的最深的深度为$c_{1}, c_{2}, c_{3}, ......, c_{cnt}$

    倍长a数组和c数组,对于每个位置$i(cnt < i <= 2 * cnt)$,找到一个最佳的点$j(i - cnt + 1 <= j <= i - 1)$。

    使得$i - j + c[j] + c[i] = i + c[i] + (c[j] - j)$的值最大。

    那么用单调队列维护$c[j] - j$的最大值即可。我为了方便偷懒用了ST表。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define	rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define	dec(i, a, b)	for (int i(a); i >= (b); --i)
    #define	fi		first
    #define	se		second
    #define	MP		make_pair
    
    typedef long long LL;
    typedef pair <int, int> PII;
    
    const int N = 1e5 + 10;
    
    struct node{
    	int x, y;
    } b[N << 1];
    
    node f[N << 1][20];
    
    int T;
    int n, cnt;
    int a[N], isroot[N], father[N], vis[N];
    int len, l, r;
    int lg[N << 1];
    int m;
    int ca = 0;
    vector <int> v[N];
    pair <int, PII> ans;
    PII  c[N];
    
    int get_circle(int x){
    	vis[x] = 1;
    	for (auto u : v[x]){
    		if (u == father[x]) continue;
    		father[u] = x;
    		if (vis[u]){
    			cnt = 0;
    			int w = x;
    			while (w ^ u){
    				a[++cnt] = w;
    				isroot[w] = cnt;
    				w = father[w];
    			}
    
    			a[++cnt] = u;
    			isroot[u] = cnt;
    			return 1;
    		}
    		if (get_circle(u)) return 1;
    	}
    
    	return 0;
    }
    
    inline node mx(const node &a, const node &b){
    	if (a.x == b.x){
    		if (a.y < b.y) return a;
    		else return b;
    	}
    
    	if (a.x > b.x) return a; else return b;
    }
    
    
    void dfs(int x, int fa, int dep){
    	if ((dep > len) || (dep == len && x < l)){
    		len = dep;
    		l   = x;
    	}
    
    	for (auto u : v[x]){
    		if (u == fa || isroot[u]) continue;
    		dfs(u, x, dep + 1);
    	}
    }
    
    void dfs2(int x, int fa, int dep, int extra){
    	if ((dep > len) || (dep == len && x < r)){
    		len = dep;
    		r   = x;
    	}
    
    	for (auto u : v[x]) if ((u != fa) && (!isroot[u] || u == extra)){
    		dfs2(u, x, dep + 1, extra);
    	}
    }
    
    inline node solve(int l, int r){
    	int k = lg[r - l + 1];
    	return mx(f[l][k], f[r - (1 << k) + 1][k]);
    }
    
    int main(){
    
    	lg[1] = 0;
    	rep(i, 2, 2e5) lg[i] = lg[i >> 1] + 1;
    
    	scanf("%d", &T);
    	while (T--){
    		scanf("%d", &n);
    		if (n == 2) while (1);
    		rep(i, 0, n + 1){
    			v[i].clear();    
    			isroot[i] = 0;
    			father[i] = 0;
    			vis[i] = 0;
    		}
    
    		cnt = 0;
    		rep(i, 1, n){
    			int x, y;
    			scanf("%d%d", &x, &y);
    			v[x].push_back(y);
    			v[y].push_back(x);
    		}
    
    		father[1] = 0;
    		get_circle(1);
    
    		memset(c, -1, sizeof c);
    		ans = MP(1e9, MP(-1, -1));
    		rep(i, 1, cnt){
    			len = -1;
    			l = -1;
    			r = -1;
    			dfs(a[i], 0, 0);
    
    			c[i] = MP(len, l);
    			len = -1;
    			dfs2(l, 0, 0, a[i]);
    			if (l > r) swap(l, r);
    			ans = min(ans, MP(2 * n - len - cnt, MP(l, r)));
    		}
    
    		rep(i, 1, cnt){
    			b[i] = {c[i].fi - i + 1, c[i].se};
    			b[i + cnt] = {c[i].fi - (i - 1 + cnt), c[i].se};
    		}
    
    		m = cnt << 1;
    		memset(f, 0, sizeof f);
    
    		rep(i, 1, m) f[i][0] = b[i];
    		rep(j, 1, 18){
    			rep(i, 1, m){
    				if ((i + (1 << j) - 1) <= m) f[i][j] = mx(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    			}
    		}
    
    		rep(i, cnt + 1, m){
    			node now = solve(i - cnt + 1, i - 1);
    			int ll = now.y, rr = c[i - cnt].se;
    			if (ll > rr) swap(ll, rr);
    			int ret = c[i - cnt].fi + i - 1 + (now.x); 
    			ans = min(ans, MP(2 * n - 2 - ret  ,   MP(ll, rr)  )    );
    		}
    		printf("Case #%d: %d %d %d
    ", ++ca, ans.fi, ans.se.fi, ans.se.se);
    	}
    
    	return 0;
    
    }
    
  • 相关阅读:
    window系统之mongoDB安装,启动及如何设置为windows服务(总结)
    永久激活pycharm 教程,方便,快捷,简单
    python classmethod方法 和 staticmethod
    Python 单元测试 之setUP() 和 tearDown()
    Git 自己的一些工作中的总结
    __str__ 和 __unicode__ 的区别和用法
    Bug 汇总
    如何理解API,API 是如何工作的
    AJAX
    集群配置虚拟主机及部署Hadoop集群碰到的问题
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/8696301.html
Copyright © 2020-2023  润新知