HDU 4126 POJ 4006 Genghis Khan the Conqueror

题意:

n(3000)个点的图  q(10^4)次操作  每次操作从原图更改一条边的权值  问q次操作后最小生成树的平均值是多少

思路:

先求最小生成树  然后讨论  如果更改的不是树边  则最小生成树不变  如果是树边  就要选择原图中的非树边和更改后的这条边其中较小的一个形成新树

难做的只有“是树边”这种情况  我们考虑  原图中的非树边与原树一定可以形成一个环  那么我们可以这样理解  只要断掉的边是环内的树边  那么都可以用这条非树边补上形成新树  也就是说  这条非树边覆盖了环内树边形成的路径!!

因此我们可以对树进行边剖分  利用线段树  达到非树边"区间覆盖"的操作  然后对于每次询问  从断开的两个点查询最小值  与更改后的边比较即可

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 3010
#define Q 10010
#define L(x) (x<<1)
#define R(x) ((x<<1)|1)
#define MID(x,y) ((x+y)>>1)
#define inf 2147483647

inline bool scand(int &ret) {
	char c;
	int sgn;
	if (c = getchar(), c == EOF)
		return 0;
	while (c != '-' && (c < '0' || c > '9'))
		c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0' && c <= '9')
		ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}

int n, m, q;
LL minCostTree, ans;
struct edge {
	int v, w, next;
} ed[N << 1];
int head[N], tot;

void add(int u, int v, int w) {
	ed[tot].v = v;
	ed[tot].w = w;
	ed[tot].next = head[u];
	head[u] = tot++;
}

int dep[N], pre[N], size[N], hson[N];
void dfs1(int u, int fa) {
	dep[u] = dep[fa] + 1;
	pre[u] = fa;
	size[u] = 1;
	for (int i = head[u]; ~i; i = ed[i].next) {
		int v = ed[i].v;
		if (v != fa) {
			dfs1(v, u);
			size[u] += size[v];
			if (size[v] > size[hson[u]])
				hson[u] = v;
		}
	}
}

int top[N], tid[N], idx;
void dfs2(int u, int tp) {
	top[u] = tp;
	tid[u] = idx;
	idx++;
	if (hson[u])
		dfs2(hson[u], tp);
	for (int i = head[u]; ~i; i = ed[i].next) {
		int v = ed[i].v;
		if (v != hson[u] && v != pre[u])
			dfs2(v, v);
	}
}

struct node {
	int l, r, w, lazy;
} f[N << 2];

void down(int i) {
	if (f[i].lazy != inf) {
		f[L(i)].w = min(f[L(i)].w, f[i].lazy);
		f[L(i)].lazy = min(f[L(i)].lazy, f[i].lazy);
		f[R(i)].lazy = min(f[R(i)].lazy, f[i].lazy);
		f[R(i)].w = min(f[R(i)].w, f[i].lazy);
		f[i].lazy = inf;
	}
}

void up(int i) {
	f[i].w = min(f[L(i)].w, f[R(i)].w);
}

void init(int l, int r, int i) {
//	cout << l << " " << r << endl;
	f[i].l = l;
	f[i].r = r;
	f[i].w = f[i].lazy = inf;
	if (l == r)
		return;
	int mid = MID(l,r);
	init(l, mid, L(i));
	init(mid + 1, r, R(i));
}

void update(int l, int r, int i, int key) {
//	cout << l << " -> " << r << " " << i << " [ " << f[i].l << " , " << f[i].r
//			<< " ]" << endl;
	if (l == f[i].l && r == f[i].r) {
		f[i].w = min(f[i].w, key);
		f[i].lazy = min(f[i].lazy, key);
		return;
	}
	down(i);
	int mid = MID(f[i].l,f[i].r);
	if (r <= mid)
		update(l, r, L(i), key);
	else if (l > mid)
		update(l, r, R(i), key);
	else {
		update(l, mid, L(i), key);
		update(mid + 1, r, R(i), key);
	}
	up(i);
}

int query(int l, int r, int i) {
	if (l == f[i].l && r == f[i].r)
		return f[i].w;
	down(i);
	int mid = MID(f[i].l,f[i].r);
	int res;
	if (r <= mid)
		res = query(l, r, L(i));
	else if (l > mid)
		res = query(l, r, R(i));
	else {
		res = query(l, mid, L(i));
		res = min(res, query(mid + 1, r, R(i)));
	}
	up(i);
	return res;
}

void Cover(int u, int v, int w) {
	int fu = top[u], fv = top[v];
	while (fu != fv) {
		if (dep[fu] < dep[fv]) {
			swap(fu, fv);
			swap(u, v);
		}
//		cout << tid[fu] << " to " << tid[u] << endl;
		update(tid[fu], tid[u], 1, w);
		u = pre[fu];
		fu = top[u];
	}
	if (u == v)
		return;
	if (dep[u] > dep[v])
		swap(u, v);
//	cout << tid[u] + 1 << " *to* " << tid[v] << endl;
	update(tid[u] + 1, tid[v], 1, w);
}

int Query(int u, int v) {
	int res = inf;
	int fu = top[u], fv = top[v];
	while (fu != fv) {
		if (dep[fu] < dep[fv]) {
			swap(fu, fv);
			swap(u, v);
		}
		res = min(res, query(tid[fu], tid[u], 1));
		u = pre[fu];
		fu = top[u];
	}
	if (u == v)
		return res;
	if (dep[u] > dep[v])
		swap(u, v);
	res = min(res, query(tid[u] + 1, tid[v], 1));
	return res;
}

int fa[N];
int getf(int x) {
	if (x != fa[x])
		fa[x] = getf(fa[x]);
	return fa[x];
}

struct edgerank {
	int u, v, w, used;
	bool operator<(const edgerank fa) const {
		return w < fa.w;
	}
} edr[N * N];
struct question {
	int u, v, w;
	bool operator<(const question ff) const {
		return w < ff.w;
	}
} ask[Q];
int main() {
	for (;;) {
		scand(n);
		scand(m);
		if (!n && !m)
			break;
		for (int i = 0; i < m; i++) {
			scand(edr[i].u);
			scand(edr[i].v);
			scand(edr[i].w);
			edr[i].u++;
			edr[i].v++;
			edr[i].used = 0;
		}
		//clear
		for (int i = 1; i <= n; i++) {
			fa[i] = i;
			head[i] = -1;
		}
		tot = 0;
		minCostTree = 0;
		//minCostTree
		sort(edr, edr + m);
		for (int i = 0, cnt = 0; i < m; i++) {
			int fu = getf(edr[i].u), fv = getf(edr[i].v);
			if (fu != fv) {
				fa[fv] = fu;
				cnt++;
				minCostTree += edr[i].w;
				add(edr[i].u, edr[i].v, edr[i].w);
				add(edr[i].v, edr[i].u, edr[i].w);
				edr[i].used = 1;
				if (cnt == n - 1)
					break;
			}
		}
		//clear
		idx = 1;
		for (int i = 1; i <= n; i++)
			hson[i] = 0;
		//heavyLight
		dfs1(1, 0);
		dfs2(1, 1);
		//input ask
		scand(q);
		for (int i = 0; i < q; i++) {
			scand(ask[i].u);
			scand(ask[i].v);
			scand(ask[i].w);
			ask[i].u++;
			ask[i].v++;
		}
		//solve
		int ide = 0;
		ans = 0;
		init(1, n, 1);
		sort(ask, ask + q);
		for (int i = 0; i < q; i++) {
			if (ask[i].u == ask[i].v) {
				ans += minCostTree;
				continue;
			}
			int intree = 0, treeedge;
			for (int j = head[ask[i].u]; ~j; j = ed[j].next) {
				if (ed[j].v == ask[i].v) {
					intree = 1;
					treeedge = ed[j].w;
					break;
				}
			}
			if (intree) {
				while (ide < m && ask[i].w > edr[ide].w) {
					if (!edr[ide].used) {
//						cout << ide << endl;
						Cover(edr[ide].u, edr[ide].v, edr[ide].w);
//						cout << ide << endl;
					}
					ide++;
				}
//				cout << i << " can update" << endl;
				int best = Query(ask[i].u, ask[i].v);
				ans += minCostTree - treeedge + min(best, ask[i].w);
//				cout << i << " can query" << endl;
			} else {
				ans += minCostTree;
			}
//			cout << i << " normal" << endl;
		}
		//output
		printf("%.4f\n", (double) (ans) / q);
	}
	return 0;
}
时间: 2024-10-09 15:34:35

HDU 4126 POJ 4006 Genghis Khan the Conqueror的相关文章

hdu 4126 Genghis Khan the Conqueror hdu 4756 Install Air Conditioning 最小生成树

这两题思路一样.先说下题意. 第一道就是一张图,q个操作,每次将一个边x,y增大到z,求出此时的最小生成树的值w,输出这q个w的平均值. 第二道是一张完全图,但是有一条未知边不能选,求最小生成树最大可能是多少. 对于第一道题,先求出最小生成树,对于每个操作x,y,z,假设x,y不是树边,那么w不变,如果是树边,那么假设这条边连接了u,v两个点集,那么只要添上一条两个点集间所有边的最小的那条即可.但是复杂度为n3,所以为了降低复杂度,要预处理出这条最小边,用dp[ i ][ j ]表示i,j两集合

hdu-4126 Genghis Khan the Conqueror(最小生成树+树形dp)

题目链接: Genghis Khan the Conqueror Time Limit: 10000/5000 MS (Java/Others)   Memory Limit: 327680/327680 K (Java/Others) Problem Description Genghis Khan(成吉思汗)(1162-1227), also known by his birth name Temujin(铁木真) and temple name Taizu(元太祖), was the fo

HDU 4126 Genghis Khan the Conqueror (树形DP+MST)

题意:给一图,n个点,m条边,每条边有个花费,给出q条可疑的边,每条边有新的花费,每条可疑的边出现的概率相同,求不能经过原来可疑边 (可以经过可疑边新的花费构建的边),注意每次只出现一条可疑的边,n个点相互连通的最小花费的期望. 析:要想连通先让他们连通起来,先构造出一个MST,然后再暴力,如果这个边不在这里面,那么花费不变,如果在里面,那我们需要知道是用原来的边最少, 还是再找一条边使他们连通起来,这里就要先预处理了,dp[i]j[i] 表示 左边的那个一半 i 和 右边那一半 j 的最长距离

HDU 4126 Genghis Khan the Conqueror MST+树形dp

题意: 给定n个点m条边的无向图. 下面m行给出边和边权 下面Q个询问. Q行每行给出一条边(一定是m条边中的一条) 表示修改边权. (数据保证修改后的边权比原先的边权大) 问:修改后的最小生成树的权值是多少. 每个询问互相独立(即每次询问都是对于原图修改) 保证没有重边. 求:所有修改后的最小生成树权值的平均值. 思路: 首先跑一个最小生成树. 求得这个MST的权值 int mst; 对于每个询问(u.v,dis); 若(u,v) 不是MST上的边,则此时的权值就是 mst 否则我们断开树边(

hdu4126 Genghis Khan the Conqueror Prim + 树形dp

好题,学到了很多新姿势. 题意:在一棵mst上,修改一些边的值(此边有可能不在MST上),Q次操作(每次只是在原图上修改),求修改后的MST总和. 题解:首先Prim 求出MST    (n^2) 对于每次修改,即相当于把MST上一条边截掉,原来的MST变成了两棵树,可以证明修改后的新MST一定包含这两棵树,也就是说只需要找到连接两棵树的最短边即可. 好了,关键问题就是怎样找连接两棵树上的最短边.可以用(n^2)的dp进行预处理,然后每次o(1)查询即可. 我们用dp[i][j] 表示 树A(包

HDU4126Genghis Khan the Conqueror(最小生成树+并查集)

Genghis Khan the Conqueror Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others) Total Submission(s): 1687    Accepted Submission(s): 501 Problem Description Genghis Khan(成吉思汗)(1162-1227), also known by his birth name

HDU 1087 &amp;&amp; POJ 2533(DP,最长上升子序列).

~~~~ 两道题的意思差不多,HDU上是求最长上升子序列的和,而POJ上就的是其长度. 貌似还有用二分写的nlogn的算法,不过这俩题n^2就可以过嘛.. ~~~~ 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1087 http://poj.org/problem?id=2533 ~~~~ HDU1087: #include<cstdio> #include<cstring> #include<algorithm> #

Eight hdu 1043 poj 1077

Description The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile mis

hdu 2844 poj 1742 Coins

hdu 2844 poj 1742 Coins 题目相同,但是时限不同,原本上面的多重背包我初始化为0,f[0] = 1;用位或进行优化,f[i]=1表示可以兑成i,0表示不能. 在poj上运行时间正好为时限3000ms....太慢了,hdu直接TLE(时限1s); 之 后发现其实并不是算法的问题,而是库函数的效率没有关注到.我是使用fill()按量初始化的,但是由于memset()可能是系统底层使用了四个字节拷 贝的函数(远比循环初始化快),效率要高得多..这就是为什么一直TLE的原因,fil