标签:cdq分治
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
Day2
写的第一个cdq的题竟然是个最小生成树+cdq我也真是醉了
自己最小生成树写的不好还硬要写
最后看了课件+别人题解才A
第一次看出来是分层图的意思但是不会构图于是学着别人vector、set、map乱搞
COGS和BZOJ过了(论BZOJ总时限的好处)
然而CODEVS无情的让我TLE了四个点(没办法乱用STL就是这个后果谁让我比较弱)
一开始向vector插入T的时候忘了初始化T的del和uni标记结果就是这个问题调了两个晚上卧槽(╯‵□′)╯︵┻━┻
用了很多最小生成树的定理
定理内容某课件里有提到
课件叫什么我忘了反正是个讲cdq分治的课件。网上有
//AC code by CreationAugust
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#define MAXN 20010
#define MAXM 50010
#define MAXINT 0x7fffffff
using namespace std;
int n,m,q;
struct edge
{
int u,v,w,num;
bool del,uni;//是否被删边或合并点
};
bool operator <(edge a,edge b)
{
return a.w<b.w;
}
vector<edge> e;
struct modify
{
int x,w;
}que[MAXM];
long long ans[MAXM];
int fa[MAXN];
int find(int a)
{
return fa[a]==a?a:fa[a]=find(fa[a]);
}
bool union_n(int a,int b)
{
int x=find(a),y=find(b);
if (x==y) return false;
fa[x]=y;return true;
}
void init(int x)
{
for (int i=0;i<=x;i++) fa[i]=i;
}
void solve(int l,int r,vector<edge> e,int n,long long sum,int newl,int newr)
{
map<int,int> hash;
int mid=(l+r)>>1;
if (l==r) newr=r;
for (int i=0;i<e.size();i++) hash.insert(make_pair(e[i].num,i));
for (int i=newl;i<=newr;i++) //修改边操作
{
modify &q=que[i];
if (hash.count(q.x))
e[hash[q.x]].w=que[i].w;
}
sort(e.begin(),e.end());
if (l==r)//找到答案
{
init(n+10);//为了保险多初始化一点
for (int i=0;i<e.size();i++) if (union_n(e[i].u,e[i].v)) sum+=e[i].w;
ans[l]=sum;
return;
}
set<int> t;//仅仅需要使用l,r之间的边
for (int i=l;i<=r;i++) t.insert(que[i].x);
init(n+10);
for (int i=0;i<e.size();i++)//缩边,将询问中的边设置为INF.有定理这时总边集e中不在生成树里的边,一定不在l,r查询的生成树中
{
if (t.count(e[i].num)) continue;//这一步相当于把该条边的边权定为无穷大,相当于删掉了该边
if (!union_n(e[i].u,e[i].v)) e[i].del=true;
}
init(n+10);
for (int i=0;i<e.size();i++)//缩点,将询问边边权设为无穷小.有定理此时总边集e中在生成树中的边必定在l,r查询的生成树中
if (t.count(e[i].num)) union_n(e[i].u,e[i].v);//删掉一条边合并两个点
for (int i=0;i<e.size();i++)
{
if (t.count(e[i].num)||e[i].del) continue;
if (union_n(e[i].u,e[i].v)) e[i].uni=true,sum+=e[i].w;//这条边一定在生成树中
}
init(n+10);//开始重构图
for (int i=0;i<e.size();i++) if (e[i].uni) union_n(e[i].u,e[i].v);//缩点
map<int,int> mp;
int top=0;
vector<edge> E;
for (int i=1;i<=n;i++)
if (find(i)==i)
mp[i]=++top;
for (int i=0;i<e.size();i++)
{
if (e[i].del||e[i].uni) continue;//不考虑一定在生成树中的和一定不在生成树中的边
edge T=e[i];T.u=mp[find(T.u)];T.v=mp[find(T.v)];
E.push_back(T);
}
solve(l,mid,E,top,sum,l,0);
solve(mid+1,r,E,top,sum,l,mid);
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=m;i++) {edge T;T.del=T.uni=0;scanf("%d%d%d",&T.u,&T.v,&T.w);T.num=i;e.push_back(T);}
for (int i=1;i<=q;i++) scanf("%d%d",&que[i].x,&que[i].w);
solve(1,q,e,n,0,1,0);
for (int i=1;i<=q;i++) printf("%lld\n",ans[i]);
}
标签:cdq分治
原文地址:http://blog.csdn.net/creationaugust/article/details/44784413