2001: [Hnoi2010]City 城市建设

2001: [Hnoi2010]City 城市建设

Time Limit: 20 Sec   Memory Limit: 162 MB
Submit: 1098   Solved: 539
[ Submit][ Status][ Discuss]

Description

PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。

Input

文件第一行包含三个整数N,M,Q,分别表示城市的数目,可以修建的道路个数,及收到的消息个数。 接下来M行,第i+1行有三个用空格隔开的整数Xi,Yi,Zi(1≤Xi,Yi≤n, 0≤Zi≤50000000),表示在城市Xi与城市Yi之间修建道路的代价为Zi。接下来Q行,每行包含两个数k,d,表示输入的第k个道路的修建代价修改为d(即将Zk修改为d)。

Output

输出包含Q行,第i行输出得知前i条消息后使城市连通的最小花费总和。

Sample Input

5 5 3
1 2 1
2 3 2
3 4 3
4 5 4
5 1 5
1 6
1 1
5 3

Sample Output

14
10
9

HINT

【数据规模】 对于20%的数据, n≤1000,m≤6000,Q≤6000。 有20%的数据,n≤1000,m≤50000,Q≤8000,修改后的代价不会比之前的代价低。 对于100%的数据, n≤20000,m≤50000,Q≤50000。

Source

[ Submit][ Status][ Discuss]



CDQ分治,,啊。。想也想不到

传送门这里的ppt有很详细的介绍了

所以就补充一点笔记吧。。

每次修改只是改了一条边的权值,因此,修改之后的MST有着极大的相似性,借此分治

定义过程Solve(l,r)为处理(l,r)的询问,然后将图修改为做完(l,r)修改之后的状态

为了很好地实现修改和询问操作,要明确一些事情,,

一开始得到图G,一个点集V和一个边集E,要做MST

假如强制E的某个子集S中所有边不选,然后做一次MST,假设得到一棵树T

那么E - T中不属于S的所有边,无论S中的所有边边权如何改变,永远都不会被选

因为我们已经能找到更优的东西,且S的改变无法影响E - T中不属于S的边

再强制S中所有边优先考虑,做一次MST,得到一棵树T'

此时T' - S的边无论S中所有边边权如何改变,永远都会存在于MST中

因为这些边是优先S的情况下选择的,S无论如何改变,只能更劣,并不影响这些边的选择

现在,定义S = {边e∈修改[l,r]中影响的边},

那么每次,我们执行操作1,删掉那些永远不会被选的边,记这个操作为Reduction

然后对于剩下的边,执行操作2,强制加入所有必选边,或是说缩点,记这个操作为Contraction

那么每次,Reduction后,剩下的边的数量最坏是|S| + n - 1,就是G - S中的边能生成一棵树

而Contraction后,剩下的点的数量最坏是|S|,也就是|S|中的边刚好无环

整个过程放在分治里,每次|S|会缩小一半,因而操作1剩下的边数最坏约是3*S,因为n和上一层分治一个数量级

3已经可以看做是常数了,那么我们只要在分治的时候Reduction,Contraction,将新图传递给子孙解决

当l == r,剩下2~3个点,一些边,此处先修改,然后做一个MST,询问就解决了,

返回的时候定义操作Recover,能抵消Reduction-Contraction的操作就行了,

显然Recover的复杂度和Reduction-Contraction一个数量级


缩点苟蒻是用安轶合并的并查集,然后Solve(l,mid)返回时对边集重新排序下

因此总的复杂度是O(nlog^n)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;

const int maxn = 2E4 + 20;
const int maxm = 5E4 + 50;
typedef long long LL;

struct E{
	int x,y,w; E(){}
	E(int x,int y,int w): x(x),y(y),w(w){}
}edgs[maxm];

struct data{
	int x,y,l; data(){}
	data(int x,int y,int l): x(x),y(y),l(l){}
}s[maxm];

int n,m,q,cnt,tp,L[maxn],Mark[maxm],po[maxm],d[maxm],fa[maxn],F[maxn];
LL Ans[maxm];

bool cmp(const int &x,const int &y) {return edgs[x].w < edgs[y].w;}
int getF(int x) {return x == F[x]?x:getF(F[x]);}
int getfa(int x) {return x == fa[x]?x:fa[x] = getfa(fa[x]);}

void Merge(int x,int y)
{
	if (L[x] > L[y]) swap(x,y);
	s[++tp] = data(x,y,L[y]);
	F[x] = y; L[y] = L[x] == L[y]?L[x]+1:L[y];
}

void Reset_fa(vector <int> v)
{
	for (int i = 0; i < v.size(); i++) {
		E e = edgs[v[i]];
		int x = getF(e.x),y = getF(e.y);
		fa[x] = x; fa[y] = y;
	}
}

void Reduction(vector <int> &v,vector <int> &g)
{
	Reset_fa(v);
	for (int i = 0; i < v.size(); i++) 
		if (Mark[v[i]] == cnt) g.push_back(v[i]);
		else {
			E e = edgs[v[i]];
			int x = getF(e.x),y = getF(e.y);
			int fx = getfa(x),fy = getfa(y);
			if (fx == fy) continue;
			fa[fx] = fy; g.push_back(v[i]);
		}
}

void Contraction(vector <int> &g,vector <int> &t,LL &tot)
{
	Reset_fa(g);
	for (int i = 0; i < g.size(); i++) {
		if (Mark[g[i]] != cnt) continue;
		E e = edgs[g[i]];
		int x = getF(e.x),y = getF(e.y);
		int fx = getfa(x),fy = getfa(y);
		if (fx == fy) continue; fa[fx] = fy; 
	}
	for (int i = 0; i < g.size(); i++) {
		if (Mark[g[i]] == cnt) {t.push_back(g[i]); continue;}
		E e = edgs[g[i]];
		int x = getF(e.x),y = getF(e.y);
		int fx = getfa(x),fy = getfa(y);
		if (fx == fy) {t.push_back(g[i]); continue;}
		tot += 1LL*e.w;
		Merge(x,y); fa[fx] = fy;
	}
}

void Recover(int cur)
{
	while (tp > cur) {
		data t = s[tp--];
		F[t.x] = t.x; L[t.y] = t.l;
	}
}

void Solve(int l,int r,vector <int> &v,LL tot)
{
	if (l == r) {
		int cur = tp;
		edgs[po[l]].w = d[l]; 
		sort(v.begin(),v.end(),cmp);
		for (int i = 0; i < v.size(); i++) {
			E e = edgs[v[i]];
			int fx = getF(e.x),fy = getF(e.y);
			if (fx == fy) continue;
			tot += 1LL*e.w; Merge(fx,fy);
		}
		Ans[l] = tot; 
		Recover(cur);
		return;
	}
	++cnt; int mid = (l + r) >> 1;
	for (int i = l; i <= r; i++) Mark[po[i]] = cnt;
	vector <int> g,t; g.clear(); t.clear();
	int cur = tp;
	Reduction(v,g);
	Contraction(g,t,tot);
	Solve(l,mid,t,tot); 
	sort(t.begin(),t.end(),cmp);
	Solve(mid+1,r,t,tot);
	Recover(cur);
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> n >> m >> q;
	for (int i = 1; i <= m; i++) {
		int x,y,w; scanf("%d%d%d",&x,&y,&w);
		edgs[i] = E(x,y,w);
	}
	for (int i = 1; i <= n; i++) F[i] = i,L[i] = 1;
	for (int i = 1; i <= q; i++) scanf("%d%d",&po[i],&d[i]);
	vector <int> v; v.clear();
	for (int i = 1; i <= m; i++) v.push_back(i);
	sort(v.begin(),v.end(),cmp);
	Solve(1,q,v,0);
	for (int i = 1; i <= q; i++) printf("%lld\n",Ans[i]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值