[BZOJ2001][HNOI2010]City城市建设-CDQ分治

来源:互联网 发布:竞价优化 编辑:程序博客网 时间:2024/06/06 03:47

City 城市建设

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。


啊咱为什么要做这道题……
感觉调到最后意识模糊完全是在对着标程改……

(╯‵□′)╯︵┻━┻


思路:
首先,考虑最小生成树的性质。
本质上,最小生成树是个贪心(咱在知乎上看到过到底是贪心还是DP的讨论)。
那么每一时刻它都将会选择当前局部最优解。

那么,从prim的角度思考:
如果咱把一些点看成一个点集S。
考虑对整个图做最小生成树T。
那么所有不在T上的、在S的补集内部连接的所有边都将毫无意义。
因为这样的一条边显然在T中具有更优解,即使S变化改变了T,也改变不了有边比它优,它毫无意义的事实。
那么,删掉这些边~

同时,从kruskal的角度,考虑T中的边。
可以发现,T中至少有一个端点位于S的补集中的所有边一定会出现在最后的最小生成树中。
因为,S集合无论怎么乱变,带来的后果只是加入或删除刚改变的这条边,并需要删除或加入一条边。原来使S的补集中的点加入最小生成树的边是不会因此改变的,即使这条边连接了S和S的补集也一样。
换句话说,对T中至少有一个端点位于S的补集中的边在最终结果中的的存在性完全不构成影响。
那么就可以考虑缩掉T上的这些不属于S集合的点了,因为它们已经被确定了~
缩成一个点就好~

于是,考虑分治询问,每次把与分治区间内修改的边相关的所有点看做S,递归下去,同步缩点即可~

#include<iostream>#include<map>#include<set>#include<vector>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>using namespace std;typedef long long ll;inline ll read(){    ll x=0,f=1;char ch=getchar();    while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();    return x;}const int N=20009;const int M=50009;const int Q=50009;struct query{    int u,w;}op[Q];struct edge{    int u,v,w,id;    bool del,un;    edge(){del=un=0;}};inline bool operator < (edge a,edge b){return a.w<b.w;}vector<edge> e;ll ans[Q];int n,m,q,fa[N];inline int find(int x){    if(fa[x]==x)return x;    return fa[x]=find(fa[x]);}inline void reset(int siz){    for(int i=1;i<=siz;i++)        fa[i]=i;}   inline bool merge(int u,int v){    int fu=find(u),fv=find(v);    if(fv==fu)return 0;    fa[fv]=fu;return 1;}inline void solve(int l,int r,vector<edge> e,int siz,ll sum,int addl,int addr){    map<int,int> ha;    int mid=l+r>>1,ei=e.size();    if(l==r)addr=r;    for(int i=0;i<ei;i++)//get edges' original name        ha[e[i].id]=i;    for(int i=addl;i<=addr;i++)//preadd edge        if(ha.count(op[i].u))            e[ha[op[i].u]].w=op[i].w;    sort(e.begin(),e.end());//sort for MST    if(l==r)//getans    {        reset(siz+9);        for(int i=0;i<ei;i++)            if(merge(e[i].u,e[i].v))                sum+=e[i].w;        ans[l]=sum;        return;    }    set<int> need;    map<int,int> hath;    reset(siz+9);    for(int i=l;i<=r;i++)//get need edges        need.insert(op[i].u);    for(int i=0;i<ei;i++)        if(!need.count(e[i].id) && !merge(e[i].u,e[i].v))//ignore need edges,use the rest to build MST            e[i].del=1;    reset(siz+9);    for(int i=0;i<ei;i++)        if(need.count(e[i].id))//ignore all edges which was in pointset S            merge(e[i].u,e[i].v);    for(int i=0;i<ei;i++)        if(!need.count(e[i].id) && !e[i].del && merge(e[i].u,e[i].v))//calc edges which must be in MST            e[i].un=1,sum+=e[i].w;    reset(siz+9);    for(int i=0;i<ei;i++)//merge points with edges which must be in MST to ignore them        if(e[i].un)            merge(e[i].u,e[i].v);    int top=0;    for(int i=1;i<=siz;i++)//get points which were not added into MST yet        if(find(i)==i)            hath[i]=++top;    vector<edge> ne;    for(int i=0;i<ei;i++)        if(!e[i].un && !e[i].del)//ignore must-in and mustn't-in edges        {            edge tmp=e[i];            tmp.u=hath[find(e[i].u)];            tmp.v=hath[find(e[i].v)];            ne.push_back(tmp);        }    solve(l,mid,ne,top,sum,l,0);    solve(mid+1,r,ne,top,sum,l,mid);//preadd l-mid optmize}int main(){    n=read();m=read();q=read();    for(int i=1;i<=m;i++)    {        edge t;        t.u=read();        t.v=read();        t.w=read();        t.id=i;        e.push_back(t);    }    for(int i=1;i<=q;i++)    {        op[i].u=read();        op[i].w=read();    }    solve(1,q,e,n,0,1,0);    for(int i=1;i<=q;i++)        printf("%lld\n",ans[i]);    return 0;}
阅读全文
0 0
原创粉丝点击