【HNOI2010】【BZOJ2001】City 城市建设2015.4.9编辑修改

来源:互联网 发布:2017钓鱼网站源码 编辑:程序博客网 时间:2024/06/06 12:48

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分治的课件。网上有
修改部分:
原课件名:《cdq分治相关》
定理引用:

假设G是一个带权无向图的边集,S是G的一个子集。 将S中边权设为+inf后,T是图G的最小生成树。 也可以说T是G-S的MST

定理1:不管S中的边权怎么变化,G的最小生成树T’将属于T∪S。 对于任意一条不属于T∪S的边,我们可以在T中找到链接它两端的一条路径。
由于这些边取值都和S无关,无论S权值怎么更改,这个环上这条边最大,不会进入MST
定理2:在定理1的前提下,我们可以在不影响T’的情况下,将G的边数缩减到n+|s|-1以下。
直接运用定理1,G-S的最小生成树最多有N-1条边。
其他的边不可能在T’中,我们可以安全地删除掉。
这一步被称为Reduction,效果是减少了G的边数。
复杂度同MST是O(mlogm),m=|G|。
定理3:不管S中的边权怎么变化。G的最小生成树T’将包含T-S。
考虑将S的权值一条边一条边提升的情况。
每提升一条边权值,MST要么不变,要么就是S中的一条边离开,一条新边加入。
无论如何,T-S这些边都不会离开MST。
定理4:在定理3的前提下,我们可以在不影响T’的情况下,将G的点数缩减到|s|+1以下。
假设已经对图进行了Reduction。
根据定理3,由于T-S的边不离开MST,我们可以将这些边连接的点合并为联通分量,将联通分量视为节点。
之后根据节点归属更新边表即可。
这一步被称为Contraction,效果是减少了G的点数。
复杂度同MST,O(mlogm)
根据上面的推论,我们可以得到结论:
给定一个图G和操作序列S,可以在O(mlogm)时间内通过reduction-contraction将图的边数缩小到n+|s|-1,点数缩小到|s|+1而保持求解过程的正确性。

//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 0x7fffffffusing 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]);}
1 0
原创粉丝点击