zoukankan      html  css  js  c++  java
  • [CF603E] Pastoral Oddities

    [题目链接]

    http://codeforces.com/contest/603/problem/E

    [题解]

    观察 : 存在一个边集满足每个点度数都是奇数的充要条件是不存在奇数个数个点的联通块。

    考虑证明奇数个点的联通块一定不合法。刚开始所有点度数均为偶数。 加入 ((u , v)) 后 :

    (u , v) 度数均为偶数 , 加入 ((u , v)) 后 , 奇度点数量 (+2)

    (u , v) 度数一奇一偶 , 加入 ((u , v)) 后 , 奇度点数量不变。

    (u , v) 度数均为奇数 , 加入 ((u , v)) 后 , 奇度点数量 (-2)

    对于偶数个点的联通块 , 直接找出一棵生成树 , 从下到上确定每条 ((u , fa(u))) 取或不取即可。

    注意到每次加入一条边 , 偶数个点的联通块只增不减。

    因此一个 (O(N ^ 2logN)) 的做法是 : 对于每个询问将边集排序 , 用并查集维护联通块奇偶性 , 依次加边加到满足条件即可。

    不妨考虑整体二分。 假设我们当前处理的是 ([l , r]) 内的边 , 且答案在 ([l , r]) 中 , 加入时间在 ([1 , l)) 且权值小于 (x) 的已经全部被加入了。 尝试求出 (ans_{mid}) , 这样就可以分治到两个子区间中了。

    首先加入 ([l , mid]) 内比 (x) 小的边 , 从小到大加入权值在 ([x , y]) 的边 ,加到满足条件时 , 我们就求出了 (ans_{mid})

    时间复杂度 : (O(MlogMlogN))

    [代码]

    #include<bits/stdc++.h>
     
    using namespace std;
     
    typedef long long LL;
     
    #define rep(i , l , r) for (int i = (l); i < (r); ++i)
     
    const int MN = 5e5 + 5;
     
    struct node {
    	int u , v , w , home;
    } E[MN] , e[MN];
     
    int N , M , ans[MN] , home[MN];
     
    inline bool cmp(node a , node b) {
    	return a.w < b.w;
    }
     
    struct DSU {
    	int fa[MN] , sz[MN] , tot , top , st[MN];
    	inline void build() {
    		tot = N;
    		for (int i = 1; i <= N; ++i) fa[i] = i , sz[i] = 1;
    	}
    	inline int find(int x) {
    		while (x != fa[x]) x = fa[x];
    		return x;
    	}
    	inline void link(int u , int v) {
    		u = find(u) , v = find(v); if (u == v) return;
    		if (sz[u] < sz[v]) swap(u , v);
    		fa[v] = u , tot -= (sz[u] & 1) , tot -= (sz[v] & 1);
    		sz[u] += sz[v]; tot += (sz[u] & 1); st[++top] = v;
    	} 
    	inline void undo() {
    		int v = st[top--] , u = fa[v];
    		tot -= (sz[u] & 1); sz[u] -= sz[v]; fa[v] = v;
    		tot += (sz[u] & 1); tot += (sz[v] & 1);
    		return;
    	} 
    } S;
     
    inline void CDQ(int l , int r , int ql , int qr) {
    	if (l > r) return;
    	int mid = l + r >> 1; int lst = S.top;
    	for (int i = l; i <= mid; ++i)
    		if (home[i] < ql) S.link(E[i].u , E[i].v);
    	for (int i = ql; i <= qr; ++i) {
    		if (e[i].home <= mid) S.link(e[i].u , e[i].v);
    		if (!S.tot) { ans[mid] = i; break; }
    	}
    	while (S.top > lst) S.undo();
    	if (!ans[mid]) {
    		for (int i = l; i <= mid; ++i)
    			if (home[i] < ql) S.link(E[i].u , E[i].v);
    		CDQ(mid + 1 , r , ql , qr);
    		while (S.top > lst) S.undo();
    		return;
    	}
    	for (int i = l; i <= mid; ++i)
    		if (home[i] < ql) S.link(E[i].u , E[i].v);
    	CDQ(mid + 1 , r , ql , ans[mid]);
    	while (S.top > lst) S.undo();
    	for (int i = ql; i < ans[mid]; ++i)
    		if (e[i].home < l) S.link(e[i].u , e[i].v);
    	CDQ(l , mid - 1 , ans[mid] , qr);
    	while (S.top > lst) S.undo();
    }
     
    int main() {
    	
    	 scanf("%d%d" , &N , &M); S.build();
    	 for (int i = 1; i <= M; ++i) {
    	 	 scanf("%d%d%d" , &e[i].u , &e[i].v , &e[i].w);
    	 	 e[i].home = i;
    	 }
    	 for (int i = 1; i <= M; ++i) E[i] = e[i];
         sort(e + 1 , e + 1 + M , cmp);
    	 for (int i = 1; i <= M; ++i)
    	 	 home[e[i].home] = i;
    	 CDQ(1 , M , 1 , M);
    	 for (int i = 1; i <= M; ++i)
    	 	 if (!ans[i]) printf("-1
    ");
    	 	 else printf("%d
    " , e[ans[i]].w);
         return 0;
    }
    
  • 相关阅读:
    在django中用MySQL为数据库 新建一个项目的流程
    django ORM中的RelatedManager(关联管理器)
    URL的命名和反向解析
    自定义分页的插件
    从数据库读出数据分页显示
    往数据库批量插入试验数据
    JDK9对集合添加的优化
    全栈工程师
    List的三个子类的特点
    List集合
  • 原文地址:https://www.cnblogs.com/evenbao/p/14469472.html
Copyright © 2011-2022 走看看