• BZOJ2654 Tree


    Time Limit: 30 Sec Memory Limit: 512 MB

    Description

    给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有(need)条白色边的生成树。

    题目保证有解。

    Input

    第一行(V),(E),(need)分别表示点数,边数和需要的白色边数。

    接下来E行,每行(s),(t),(c),(col)表示这边的端点(点从(0)开始标号),边权,颜色((0)白色(1)黑色)。

    Output

    一行表示所求生成树的边权和。

    (Vleq50000),(Eleq100000),所有数据边权为([1,100])中的正整数。

    Sample Input

    2 2 1
    0 1 1 1
    0 1 2 0
    

    Sample Output

    2
    

    HINT

    原数据出错,现已更新 by liutian,但未重测---2016.6.24

    Solution

    比较神仙的做法,自己暂时还是不能想到的。路还长着,也还要多做点题目来提升提升自己的思维能力。这道题先Mark着。

    我们发现,如果将白边的权一起增加或减少一个值,那么不会对选择的黑边有影响。至于证明嘛,自己意会一下就好了嘛,应该都没问题吧,就不要太严格地证了。

    那么我们二分白边增减的值,然后对修改后的图做最小生成树。如果出现最小生成树里面的白边数量刚好就是(need)的话,那么这个树的形态就是题目要求的最小生成树了。

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    #define lowbit(x) ((x)&(-(x)))
    #define REP(i,a,n) for(register int i=(a);i<=(n);++i)
    #define PER(i,a,n) for(register int i=(a);i>=(n);--i)
    #define FEC(i,x) for(register int i=head[x];i;i=g[i].ne)
    #define dbg(...) fprintf(stderr,__VA_ARGS__)
    namespace io{
    	const int SIZE=(1<<21)+1;char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];int f,qr;
    	#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
    	inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
    	inline void putc(char x){*oS++=x;if(oS==oT)flush();}
    	template<class I>inline void read(I &x){for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;}
    	template<class I>inline void write(I x){if(!x)putc('0');if(x<0)putc('-'),x=-x;while(x)qu[++qr]=x%10+'0',x/=10;while(qr)putc(qu[qr--]);}
    	struct Flusher_{~Flusher_(){flush();}}io_flusher_;
    }//orz laofudasuan
    using io::read;using io::putc;using io::write;
    typedef long long ll;typedef unsigned long long ull;
    template<typename A,typename B>inline bool SMAX(A&x,const B&y){return y>x?x=y,1:0;}
    template<typename A,typename B>inline bool SMIN(A&x,const B&y){return y<x?x=y,1:0;}
    
    const int N=50000+7,M=100000+7;
    int n,m,nd,x,y,z,opt,cnt1,cnt2,ans;
    struct Graph{int x,y,z;bool operator<(const Graph&a)const{return z<a.z;}}bl[M],wh[M];//错误笔记:一定要记得打上const 
    
    int fa[N];
    inline int Find(int x){return fa[x]==x?x:fa[x]=Find(fa[x]);}
    inline void Union(int x,int y){x=Find(x),y=Find(y);x!=y?fa[y]=x:0;}
    inline bool Check(int k){
    	for(register int i=1;i<=n;++i)fa[i]=i;
    	int cnt=0,get=0;ans=0;
    	for(register int i1=1,i2=1;i1<=cnt1||i2<=cnt2;){
    		if(i1<=cnt1&&(i2>cnt2||bl[i1].z<wh[i2].z+k))x=Find(bl[i1].x),y=Find(bl[i1].y),z=bl[i1].z,++i1;else x=Find(wh[i2].x),y=Find(wh[i2].y),z=wh[i2].z+k,++i2,x!=y?++get:0;//错误笔记:注意判断里面是i2>cnt2不是cnt1 
    		if(x==y)continue;++cnt;
    		Union(x,y);ans+=z;if(cnt==n-1)break;
    	}
    	return get>=nd;
    }
    inline int Solve(){
    	int l=-100,r=100;//错误笔记:这里的范围是-100..100,不是1..100。一开始以为只会出现直接选选的边多于need,实际上也有可能少于,都要考虑。 
    	while(l<r){
    		int mid=(l+r+1)>>1;
    		if(Check(mid))l=mid;
    		else r=mid-1;
    	}Check(l);
    	return ans-nd*l;
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("BZOJ2654.in","r",stdin);
    	freopen("BZOJ2654.out","w",stdout);
    #endif
    	read(n),read(m);read(nd);
    	for(register int i=1;i<=m;++i){//错误笔记:m是边数,n是点数! 
    		read(x),read(y),read(z),read(opt);++x,++y;//错误笔记:点从0开始编号 
    		if(opt)bl[++cnt1]=Graph{x,y,z};
    		else   wh[++cnt2]=Graph{x,y,z};
    	}
    	sort(bl+1,bl+cnt1+1);sort(wh+1,wh+cnt2+1);
    	write(Solve());putc('
    ');
    }
    
  • 相关阅读:
    mfc启动画面
    个人冲刺第十天
    个人冲刺第九天
    个人冲刺第八天
    个人冲刺第七天
    个人冲刺第六天
    新一周冲刺计划2
    新一周冲刺
    创意1
    团队绩效与目标
  • 原文地址:https://www.cnblogs.com/hankeke/p/9833784.html
Copyright © 2020-2023  润新知