首页 > 其他分享 >CF603E Pastoral Oddities 题解

CF603E Pastoral Oddities 题解

时间:2024-11-17 15:57:14浏览次数:1  
标签:rnk sz int 题解 fx std CF603E fy Oddities

Description

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

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

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

\(n \le 10^5\),\(m \le 3 \times 10^5\)。

Solution

考虑给定一个图,怎么判断这个图存在一个边集满足条件。

结论是这个图的每个连通块大小为偶数就合法,否则不合法。证明就考虑如果存在一个连通块大小为奇数,则这个连通块最终总度数一定为奇数,而显然加入一条边所有点的度数和奇偶性不变,仍为偶数,所以矛盾。

如果一个连通块大小为偶数,就随便拿出一个生成树,然后从叶子向根考虑。如果一个点儿子连过来的边有偶数个,这个点就连父亲,否则不连。剩下的根节点也一定满足条件。

所以如果固定边集的可选范围,只需要先对于边权从小到大排序,在加边的过程中维护奇连通块的个数即可。

但是如果需要动态加边,上面那个做法就没用了,因为你无法确定某一个时刻的连通块状态。

考虑线段树分治。

由于答案从后往前不降,所以第 \(i\) 条边在最优边集中存在的时间一定是一个以 \(i\) 为左端点区间。线段树分治时先分治右子树,再分治左子树,到叶子时如果存在奇连通块就加入新边,直到不存在奇连通块。由于在加边的过程中新加入的边的存在时间被确定,所以在线段树上更新即可。

时间复杂度:\(O(m\log n\log m)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5, kMaxM = 3e5 + 5;

int n, m, p, cnt_odd;
int ans[kMaxM];
std::tuple<int, int, int, int> ed[kMaxM];
std::vector<std::pair<int, int>> vec[kMaxM * 4];

struct DSU {
  int fa[kMaxN], sz[kMaxN], rnk[kMaxN];
  std::vector<std::tuple<int, int, int>> vec;

  void init() {
    for (int i = 1; i <= n; ++i)
      fa[i] = i, sz[i] = 1, rnk[i] = 0;
    cnt_odd = n;
  }

  void back(int t) {
    for (; vec.size() > t; vec.pop_back()) {
      auto [fx, fy, det] = vec.back();
      cnt_odd -= (sz[fy] & 1);
      fa[fx] = fx, rnk[fy] -= det, sz[fy] -= sz[fx];
      cnt_odd += (sz[fx] & 1) + (sz[fy] & 1);
    }
  }

  int find(int x) { return x == fa[x] ? x : find(fa[x]); }
  void unionn(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx == fy) return;
    if (rnk[fx] > rnk[fy]) std::swap(fx, fy);
    int det = (rnk[fx] == rnk[fy]);
    cnt_odd -= (sz[fx] & 1) + (sz[fy] & 1);
    fa[fx] = fy, rnk[fy] += det, sz[fy] += sz[fx];
    vec.emplace_back(fx, fy, det);
    cnt_odd += (sz[fy] & 1);
  }
} dsu;

void update(int x, int l, int r, int ql, int qr, std::pair<int, int> ed) {
  if (l > qr || r < ql) return;
  else if (l >= ql && r <= qr) return void(vec[x].emplace_back(ed));
  int mid = (l + r) >> 1;
  update(x << 1, l, mid, ql, qr, ed), update(x << 1 | 1, mid + 1, r, ql, qr, ed);
}

void solve(int x, int l, int r) {
  int t = dsu.vec.size();
  for (auto [u, v] : vec[x]) dsu.unionn(u, v);
  if (l == r) {
    for (; p < m && cnt_odd;) {
      ++p;
      auto [w, u, v, id] = ed[p];
      if (id <= l) {
        dsu.unionn(u, v);
        update(1, 1, m, id, l - 1, {u, v});
      }
    }
    if (!cnt_odd) ans[l] = std::get<0>(ed[p]);
    else ans[l] = -1;
  } else {
    int mid = (l + r) >> 1;
    solve(x << 1 | 1, mid + 1, r);
    solve(x << 1, l, mid);
  }
  dsu.back(t);
}

void dickdreamer() {
  std::cin >> n >> m;
  for (int i = 1; i <= m; ++i) {
    int u, v, w;
    std::cin >> u >> v >> w;
    ed[i] = {w, u, v, i};
  }
  std::sort(ed + 1, ed + 1 + m);
  dsu.init();
  solve(1, 1, m);
  for (int i = 1; i <= m; ++i) std::cout << ans[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:rnk,sz,int,题解,fx,std,CF603E,fy,Oddities
From: https://www.cnblogs.com/Scarab/p/18550640

相关文章

  • 2021 Hubei Provincial Collegiate Programming Contest/2021湖北省赛 题解
    按解决顺序排列目录FAIDHECKJGBF二分答案ans,放最小的前ans个bi(变成必须放完)因为bi=2^k,所以小的放了可能会拆散大的空间,大的把小的地方占了的话小的可以塞其他地方,所以先放大的然后暴力能放则放,最多log次指针回到开头所以一次求解O(nlogn),总复杂度log^2A模拟,暴力枚举暴力异......
  • ABC380题解(F&G)
    ABC380F.ExchangeGame因为\(n+m+k\leq12\),考虑状压dp,设\(f(x,s1,s2,s3)\)表示先手,后手,桌子上的牌分别是哪一些,这有\(O(3^n)\)种状态。然后只要枚举出哪一张即可,有\(f(s1,s2,s3)\tof(s2,s1-i+j,s3+i-j)(i\ins1,j\ins3,a_j<a_i)\)\(f(s1,s2,s3)\tof(s2,s1-i,s3+i......
  • 2024ICPC南京部分题解
    LeftShifting3题面:给定一个长度为\(n\)的字符串\(S=s_0s_1\cdotss_{n-1}\),你可以将\(S\)向左移动最多\(k\)次(包括零次)。计算在操作后字符串中包含的“nanjing”子字符串的最大数量。更正式地说,让\(f(S,d)\)成为将\(S\)向左移动\(d\)次得到的字符串。也就是......
  • NOIP集训 P4137 Rmq Problem / mex 题解
    前置指使:可持久化线段树题解:P4137RmqProblem/mex有一个长度为\(n\)的数组\(\{a_1,a_2,...,a_n\}\)。\(m\)次询问,每次询问一个区间内最小没有出现过的自然数。Input第一行,两个正整数\(n,m\)。第二行,\(n\)个非负整数\(a_1,a_2,...,a_n\)。接下来\(m\)行,每......
  • 蓝桥杯模拟赛(第一期)个人题解&感想
    林专大一牲第一次写blog,更新较慢,写的不好的地方请见谅(好多题目忘记了题号and暂时没有题目要求的内容…后面会补充的!)本次带来的是蓝桥杯模拟赛第一期的个人题解(笨人水平较低,大家可以在评论区指出错误/讨论更优解~)大多题我是用c++写的,但也掺了几道c,为以后全面用c++打比赛作过......
  • AI|经常崩溃的问题解决
    AdobeIllustratorCrashes网络上大部分说法都是重装AI,兼容性问题,管理员权限或者是版本不对,经测试均无效,甚至出现重装系统这种离谱的办法,正确的解决办法是把首选项的性能里的GPU取消勾选,或者再把电脑的虚拟内存扩大即可。 Step1:打开首选项 Step2:取消勾选GPU性能......
  • CF987 Div2 F 题解
    阶段1考虑我们每次随机删除两个然后询问,若中位数为\(\frac{n}{2},\frac{n}{2}+1\)称被删除的两个为基准数,用\(v_1,v_2\)代表。每次询问得到解的概率约为\(\frac{1}{2}\)。发现基准数一定一个\(<\frac{n}{2}\)一个\(>\frac{n}{2}+1\),且对于一次四个数的询问\(x......
  • [AGC018C] Coins 题解
    先全部选\(a_i\),假设\(b_i=b_i-a_i,c_i=c_i-a_i\)。那么题目转化为选\(y\)个\(b\)和\(z\)个\(c\)使得答案最大。会发现若\(i\)位置选的\(b\),\(j\)位置选的\(c\),那么需要满足\(b_i-c_i>b_j-c_j\)。我们按上述条件排序,这样一定是在左边选\(b\),右边选\(c\)。那......
  • 题解:ABC013D 阿弥陀
    先考虑\(D=1\),明显可以\(O(M)\)模拟预处理出在最上面的每个位置往下走会走到什么位置,之后每次就可以\(O(1)\)查询了。当\(D>1\)时,可以依赖预处理的数据每次\(O(D)\)暴力查询。又注意到每次对所有位置进行的变换操作是一样的,可以倍增后用类似快速幂的方法优化到\(O(\log......
  • CF2031D 题解
    原题链接最后悔的一集,感觉D\(<\)everything。考虑由确定的点推出其他点的答案,发现最高点的答案是确定的,设其位置为\(x\)。然后根据题目定义,发现可以分成\([1,x-1],[x,n]\)两个区间,\([x,n]\)答案均为\(h_x\)。对于\([1,x-1]\)区间,我们找到第一个\(>[x,n]\)区间最小......