[分治][并查集]Codeforces 603E. Pastoral Oddities

12 篇文章 0 订阅
9 篇文章 0 订阅

Description D e s c r i p t i o n

  • n n 个点的图,你要动态的加入m条边。
  • 每次加边后询问图中找到一个最大边最小的子图使得每个点的度数都是奇数。
  • 输出最大边的最小值。
  • n105,m3×105 n ≤ 10 5 , m ≤ 3 × 10 5

Solution S o l u t i o n

有一个结论:满足条件的图当且仅当每个联通块大小为偶数。
又是这个 idea idea 哦。证明的话可以考虑图的生成树,从下至上调整。
然后答案一定是非上升的。
可以考虑分治。道理是和决策单调性DP分治差不多的
定义分治 DivAndConq(l,r,lo,hi) D i v A n d C o n q ( l , r , l o , h i ) 表示当前分治的边集为 [l,r] [ l , r ] ,答案在 [lo,hi] [ l o , h i ] 中。
这里有一张图很好的说明了分治结构。
这里写图片描述
因为要维护联通块个数的奇偶性,就要用并查集。
这个就和这道题有些类似啦。
那其实这道题就也是可以用LCT做的了。
并查集的时间复杂度是 o(mlogmlogn) o ( m log ⁡ m log ⁡ n ) 的。

#include <bits/stdc++.h>
using namespace std;

const int N = 101010;
const int M = 303030; 
typedef long long ll;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0;
    for (c = get(); c < '0' || c > '9'; c = get());
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}

struct Qry {
    int x, y, l, id;
    inline bool operator <(const Qry &a) const{
        return l < a.l;
    }
};
Qry p[M], q[M];
int n, m;
int ans[M];
namespace dsu {
    int fa[N], rk[N], sz[N];
    int sta[N], drk[N], dsz[N], dcnt[N];
    int top, cnt;
    inline void Init(int n) {
        for (int i = 1; i <= n; i++) fa[i] = i;
        for (int i = 1; i <= n; i++) sz[i] = 1;
        cnt = n;
    }
    inline int Fa(int x) {
        while (fa[x] != x) x = fa[x];
        return x;
    }
    inline int Merge(int x, int y) {
        static int f1, f2;
        f1 = Fa(x); f2 = Fa(y);
        if (f1 == f2) return 0; ++top;
        dsz[top] = dcnt[top] = drk[top] = 0;
        if (rk[f1] > rk[f2]) swap(f1, f2);
        if (rk[f1] == rk[f2]) {
            ++rk[f2]; drk[top] = 1;
        }
        fa[f1] = f2; sta[top] = f1;
        if (sz[f1] && sz[f2]) {
            cnt -= 2; dcnt[top] = -2;
        }
        dsz[top] = sz[f1]; sz[f2] ^= sz[f1];
        return 1;
    }
    inline void Restore(int pos) {
        while (top > pos) {
            cnt -= dcnt[top];
            rk[fa[sta[top]]] -= drk[top];
            sz[fa[sta[top]]] ^= dsz[top];
            fa[sta[top]] = sta[top]; --top;
        }
    }
    inline bool Check(void) {
        return !cnt;
    }
    inline void Record(int &lst) {
        lst = top;
    }
}

inline void DivAndConq(int l, int r, int lo, int hi) {
    int mid = l + r >> 1, mans = -1, lst;
    if (l > r) return; dsu::Record(lst);
    for (int i = l; i <= mid; i++)
        if (q[i].id < lo) dsu::Merge(q[i].x, q[i].y);
    for (int i = lo; i <= hi; i++)
        if (p[i].id <= mid) {
            dsu::Merge(p[i].x, p[i].y);
            if (dsu::Check()) {
                mans = i; break;
            }
        }
    dsu::Restore(lst);
    if (mans == -1) {
        for (int i = l; i <= mid; i++) ans[i] = -1;
        for (int i = l; i <= mid; i++)
            if (q[i].id < lo) dsu::Merge(q[i].x, q[i].y);
        DivAndConq(mid + 1, r, lo, hi);
        return dsu::Restore(lst);
    }
    ans[mid] = p[mans].l;
    for (int i = l; i <= mid; i++)
        if (q[i].id < lo) dsu::Merge(q[i].x, q[i].y);
    DivAndConq(mid + 1, r, lo, mans);
    dsu::Restore(lst);
    for (int i = lo; i < mans; i++)
        if (p[i].id < l) dsu::Merge(p[i].x, p[i].y);
    DivAndConq(l, mid - 1, mans, hi);
    dsu::Restore(lst);
}

int main(void) {
    read(n); read(m);
    for (int i = 1; i <= m; i++) {
        read(p[i].x); read(p[i].y); read(p[i].l);
        p[i].id = i; q[i] = p[i];
    }
    sort(p + 1, p + m + 1);
    for (int i = 1; i <= m; i++)
        q[p[i].id].id = i;
    dsu::Init(n);
    DivAndConq(1, m, 1, m);
    for (int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值