码迷,mamicode.com
首页 > 其他好文 > 详细

【HNOI2010】【BZOJ2001】City 城市建设

时间:2015-03-31 22:27:10      阅读:175      评论:0      收藏:0      [点我收藏+]

标签: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]);
}

【HNOI2010】【BZOJ2001】City 城市建设

标签:cdq分治

原文地址:http://blog.csdn.net/creationaugust/article/details/44784413

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!