zoukankan      html  css  js  c++  java
  • [APIO2013]道路费用

    II.[APIO2013]道路费用

    这个(k),明显就很可以(2^k)枚举掉有哪些边出现在MST上。但是,如何设置权值使得要求出现的边全数出现呢?

    我们考虑裸的Kruskal算法。首先,先用冰茶姬将要求出现的边加入生成树(明显此时如果已经出现了环,则此边集本身即不合法,可以直接跳过该边集),然后将原图中的边按照权值排序后依次加入生成树。

    假如添加一条原图中的边使得两个连通块联通,显然这条边应该出现在MST上;否则,该边便不应该出现在MST上。而这个的充要条件,便是现在的MST上,该边连接的两个节点间的路径中,所有边的权值都不大于当前试图加入的边的权值。于是就直接遍历路径上所有边并让其边权与当前边权取(min)即可。

    明显该算法是(2^knm)的,就算使用树上倍增也是(2^kmlog n)的,根本不可能通过所有数据。考虑优化。

    观察到(k)条边最多与(2k)个点有关,换言之无论它们有哪些边在MST中,它们所影响的只是少部分节点。这就引起了一个重要推论——在所有(2^k)个MST中,有大部分边是相同的。那么,具体是哪些边相同呢?

    我们考虑先将(k)条边全数加入MST中(不管出不出现环),得到一张图。现在如果我们加入了更少的边,明显该图中的连通块数只会更多不会更少;故此时MST中还需添加的边集,一定是所有(2^k)种MST的公共边集。于是我们就可以预先把这些公共边集加入生成树(换言之,就是把原图缩点)。在缩完点后,明显此图最多剩余(k+1)个连通块(即,考虑一棵包含所有(k)条边的MST,然后再拆去此(k)条边,最多就剩余(k+1)个连通块)。

    假如我们加入的新边是(k)条边的某个子集的话,明显它可能并不能使此(k+1)个连通块全数联通;故我们需要再加入一些原边。而此处的原边,一定是此(k+1)个连通块的MST上的边,即最多(k)条边。

    于是我们只需首先把被钦定的子集中的边加入MST(如上文所说,记得判环),然后依次考虑该(k)条原图中的边并尝试加入MST即可。

    此时,该算法便被优化到了(2^kk^2)。使用树上倍增,可以优化到(2^kklog k),但明显因为(k)过小,该优化没有任何意义。直接使用(O(2^kk^2))的算法即可通过。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,p,q,r,dsu[100100],col[100100];
    ll num[100100],res,now;
    void init(int len){for(int i=1;i<=len;i++)dsu[i]=i;}
    int find(int x){return dsu[x]==x?x:dsu[x]=find(dsu[x]);}
    bool merge(int x,int y){x=find(x),y=find(y);if(x==y)return false;dsu[y]=x;return true;}
    struct edge{
    	int x,y,z;
    	edge(int X=0,int Y=0,int Z=0){x=X,y=Y,z=Z;}
    	friend bool operator <(const edge &u,const edge &v){return u.z<v.z;}
    }e[300100];
    int u[300100],v[300100],w[300100];
    bool on[300100];
    vector<int>g[300100];
    bool dfs1(int x,int fa,int dest,int lim){
    	if(x==dest)return true;
    	for(auto i:g[x]){
    		int y=(u[i]^v[i]^x);
    		if(y==fa)continue;
    		if(dfs1(y,x,dest,lim)){w[i]=min(w[i],lim);return true;}
    	}
    	return false;
    }
    ll dfs2(int x,int fa){
    	ll ret=num[x];
    	for(auto i:g[x]){
    		int y=(u[i]^v[i]^x);
    		if(y==fa)continue;
    		ll tmp=dfs2(y,x);
    		if(i<p)now+=tmp*w[i];
    		ret+=tmp;
    	}
    	return ret;
    }
    void func(int mask){
    	init(r);
    	for(int i=1;i<=r;i++)g[i].clear();
    	for(int i=0;i<p;i++)if((mask&(1<<i))&&merge(u[i],v[i]))g[u[i]].push_back(i),g[v[i]].push_back(i),w[i]=0x3f3f3f3f;
    	for(int i=p;i<q;i++)if(merge(u[i],v[i]))g[u[i]].push_back(i),g[v[i]].push_back(i);else dfs1(u[i],0,v[i],w[i]);
    	now=0;
    	dfs2(col[1],0);
    	res=max(res,now);
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&p);
    	for(int i=0,x,y,z;i<m;i++)scanf("%d%d%d",&x,&y,&z),e[i]=edge(x,y,z);
    	sort(e,e+m);
    	init(n);
    	for(int i=0;i<p;i++)scanf("%d%d",&u[i],&v[i]),merge(u[i],v[i]);
    	for(int i=0;i<m;i++)on[i]=merge(e[i].x,e[i].y);
    	init(n);
    	for(int i=0;i<m;i++)if(on[i])merge(e[i].x,e[i].y);
    	for(int i=1;i<=n;i++)if(dsu[i]==i)col[i]=++r;
    	for(int i=1,x;i<=n;i++)col[i]=col[find(i)],scanf("%d",&x),num[col[i]]+=x;
    	q=p;
    	for(int i=0;i<m;i++)if(merge(e[i].x,e[i].y))u[q]=col[e[i].x],v[q]=col[e[i].y],w[q]=e[i].z,q++;
    	for(int i=0;i<p;i++)u[i]=col[u[i]],v[i]=col[v[i]];
    	for(int i=1;i<(1<<p);i++)func(i);
    	printf("%lld
    ",res);
    	return 0;
    }
    

  • 相关阅读:
    读保哥《ASP.NET MVC2开发实战》第三回(Controller1)
    MVC Model之简单数据问题
    读保哥《ASP.NET MVC2开发实战》第三回(Controller2)
    [导入]我的google十年散乱的记忆(转载)
    [导入]从飞信到SNS,中国移动进军互联网之路,悬乎
    [导入]预览google chrome新功能
    [导入]百度曲线杀入视频分享领域
    [导入]TMobile官方正式宣布将在9月23日举行android新闻发布会
    [导入]Google手机水货下个月开卖 零售价格4000元
    [导入]祝大家中秋节快乐
  • 原文地址:https://www.cnblogs.com/Troverld/p/14621781.html
Copyright © 2011-2022 走看看