zoukankan      html  css  js  c++  java
  • CF603E 整体二分

    给定一张 (n) 个点的无向图,初始没有边。

    依次加入 (m) 条带权的边,每次加入后询问是否存在一个边集,满足每个点的度数均为奇数。

    若存在,则还需要最小化边集中的最大边权。

    (n le 10^5),(m le 3 imes 10^5)

    显然答案是随着边数的增加而不下降的。

    看每个点度数都是奇数意味着什么。可以发现他和无奇数大小的连通块互为充要条件。随便证明一下。

    若连通块大小为奇数,那么意味着度数和也为奇数,而度数和必然为 (2*m) ,发生矛盾。

    若联通块大小为偶数,随便弄一个生成树出来,可以构造出一个度数全为奇数的森林。

    我们维护一个可撤销并查集,并且维护奇数连通块数量。容易发现,多连边不会使答案更劣。

    整体二分,每次求出查询区间中间元素的答案,即可获得两边的答案范围。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int read()
    {
    	int a = 0,x = 1;char ch = getchar();
    	while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
    	while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
    	return a*x;
    }
    int n,m;
    
    namespace DSU{
    	const int N=1e6+7;
    	int fa[N],siz[N],top,sum;
    	struct Node{int a,fa;}stk[N];
    	void init(int n) {
    		for(int i = 1;i <= n;i ++) siz[i] = 1,fa[i] = i;sum = n;
    	}
    	int find(int s) {return fa[s] == s ? s : find(fa[s]);}
    	void merge(int a,int b)
    	{
    		a = find(a),b = find(b);
    		if(a == b) return ;
    		if(siz[a] > siz[b]) swap(a,b);
    		sum -= ((siz[a]&1)&&(siz[b]&1))*2;
    		fa[a] = b;siz[b] += siz[a];
    		stk[++top] = (Node){a,b};
    	//	printf("add(%d,%d),sum = %d
    ",a,b,sum);
    	}
    	void BACK()
    	{
    		if(!top) return ;
    		int a = stk[top].a,b = stk[top].fa;
    		fa[a] = a,siz[b] -= siz[a];-- top;
    		sum += ((siz[a]&1)&&(siz[b]&1))*2;
    	//	printf("del(%d,%d),sum = %d
    ",a,b,sum);
    	}
    }
    using namespace DSU;
    int ans[N];
    struct node{
    	int u,v,w,id;
    }p[N],q[N];
    bool cmp(node a,node b) {return a.w < b.w;}
    
    void solve(int L,int R,int l,int r)
    {
    	if(L>R) return ;
    	int MID = L+R>>1,cur = top;
    	for(int i = L;i <= MID;i ++) if(p[i].w <= l) merge(p[i].u,p[i].v);
    	int NOW=l;for(int i = l;i <= r && sum;i ++,NOW++) {
    		if(q[i].id <= MID) merge(q[i].u,q[i].v);//printf("NOW=%d   ==
    ",NOW);
    	}
    	NOW = max(NOW-1,l);//printf("NOW=%d
    ",NOW);
    	if(!sum) ans[MID] = NOW;
    	else ans[MID] = -1;
    	while(top > cur) BACK();
    	for(int i = l;i <= NOW;i ++) if(q[i].id <= L) merge(q[i].u,q[i].v);
    	solve(L,MID-1,NOW,r);
    	while(top > cur) BACK();
    	for(int i = L;i <= MID;i ++) if(p[i].w <= l) merge(p[i].u,p[i].v);
    	solve(MID+1,R,l,NOW);
    	while(top > cur) BACK();
    }
    
    int main()
    {
    //	freopen("random.in","r",stdin);
    //	freopen("sol.out","w",stdout);
    	n = read(),m = read();
    	for(int i = 1;i <= m;i ++) {
    		int u = read(),v = read(),w = read();
    		p[i] = q[i] = (node){u,v,w,i};
    	}
    	sort(q+1,q+1+m,cmp);init(n);
    	for(int i = 1;i <= m;i ++) p[q[i].id].v = i;
    	solve(1,m,1,m);
    	for(int i = 1;i <= m;i ++) printf("%d
    ",ans[i]>0?q[ans[i]].w:-1);
    	return 0;
    }
    
  • 相关阅读:
    sql中的并、交、差
    白水的sql需求:每个病人的对应最小诊断类别|partition|
    |转|oracle行转列点评oracle11g sql新功能pivot/unpivot
    oracle wm_concat(column)函数的使用不同公司支付同一客商(行转列)|转|
    记录那些删除的伴我成长的触发器
    PL/SQL Developer使用技巧|F=Forwarding|
    手工创建数据库 ocp课程 wait for
    Oracle数据库监听配置|转|
    最新解决的sql:病人相邻两次看病小于3天
    tns的一些常见错误分析实例 |转|
  • 原文地址:https://www.cnblogs.com/nao-nao/p/14838580.html
Copyright © 2011-2022 走看看