码迷,mamicode.com
首页 > 其他好文 > 详细

Open_POJ C15C Rabbit's Festival

时间:2015-11-27 12:31:48      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:

http://poj.openjudge.cn/practice/C15C?lang=en_US

n 点 m 边 k 天。

每条边在某一天会消失(仅仅那一天消失)。

问每一天有多少对点可以相互到达。

 

这个一看就是并查集。但是呢。。。。

“并查集是不能删边的”,这是这个问题的障碍。

并查集真的不能删边么?其实不是绝对的,如果删除的边是最后加入的边,并且并查集不进行路径压缩的话,自然是可以删除的。

P.S. 并查集找根如果路径压缩加按秩合并的话是 $O(log(log(n)))$,如果只按秩合并的话是 $O(log(n))$。

 

那么怎么让安排顺序,让并查集可以删除的边都是最后加入的边呢?这就用到了 cdq 分治(P.S. cdq 前辈提出的一类分治)。

考虑处理 1-10 天每天的连通对数目,并且最后将 1-10天的边都正确退出并查集的过程记做 cdq(1,10)。

那么 cdq(1, 10) 有如下的过程:

1. 6-10 的边加入并查集。

2. cdq(1, 5)

3. 6-10 的边退出并查集

4. 1-5 的边加入并查集

5. cdq(6, 10)

6. 1-5 的边退出并查集

 

注意:

0. 递归一直进行,直到 cdq(i, i) 的状况。这个时候就会发现,当前除了第 i 天的边,其他的边都加在并查集里。

1. 途中维护一个数 result,表示当前并查集中连通对的数目。如果是 cdq(i,i) 的话,其实什么都不用做,只要输出 result 就可以了。

2. 第 3 步删边的时候,应该反第 1 步的加边的顺序。为此,用一个栈存当前合并的集合根对。在 1 的时候记录栈顶位置 tmp,3 的时候反向退栈直到 tmp。 4 与 6 也是类似的。

3. 找根复杂度是 $log(n)$,cdq(l, r) 复杂度是 $O((r-l)log(n))$ 所以总复杂度应该是 $O(klog^2(n))$

 

另:

1. 一开始没注意顺序,瞎写。

2. Vjudge 说这个题 64 位整数的占位符是 %I64d %I64u,不要信它。

#include <stdio.h>
#include <vector>
#include <algorithm>

using std::vector;

const int MAXN = 100000 + 5;
const int MAXM = 200000 + 5;

int fa[MAXN];
int cnt[MAXN];

void initSets(int n)
{
  for (int i = 1; i <= n; i++) {
    fa[i] = i;
    cnt[i] = 1;
  }
}

int root(int i)
{
  for (; i - fa[i]; i = fa[i]);
  return i;
}

struct Edge
{
  int x, y;
};

vector<vector<Edge> > D;

long long c2(int x)
{ return 1LL * x * (x - 1) / 2; }

struct CDQ
{
  std::pair<int, int> stk[MAXM];
  int stkTop;

  long long result;

  void push(Edge& now)
  {
    int a = root(now.x);
    int b = root(now.y);
    if (a == b)
      return;
    result -= c2(cnt[a]);
    result -= c2(cnt[b]);
    if (cnt[a] > cnt[b])
      std::swap(a, b);
    // a -> b
    cnt[b] += cnt[a];
    result += c2(cnt[b]);
    fa[a] = b;
    stkTop++;
    stk[stkTop].first = a;
    stk[stkTop].second = b;
  }

  void push(int l, int r)
  {
    for (int i = l; i <= r; i++)
      for (int j = 0; j < D[i].size(); j++)
        push(D[i][j]);
  }

  void pull(int l, int r)
  {
    for (int i = r; i >= l; i--) {
      int s = stk[i].first;
      int r = stk[i].second;
      result -= c2(cnt[r]);
      cnt[r] -= cnt[s];
      result += c2(cnt[s]);
      result += c2(cnt[r]);
      fa[s] = s;
    }
  }

  void cdq(int l, int r)
  {
    if (l == r) {
      printf("%lld\n", result);
      return;
    }
    int mid = (l + r) >> 1;
    int tmp = stkTop;
    push(mid + 1, r);
    cdq(l, mid);
    pull(tmp + 1, stkTop);
    stkTop = tmp;

    push(l, mid);
    cdq(mid + 1, r);
    pull(tmp + 1, stkTop);
    stkTop = tmp;
  }

  CDQ(int k, long long result)
  {
    stkTop = 0;
    this -> result = result;
    cdq(1, k);
  }
};

int main()
{
  int n, m, k;
  for (; scanf("%d %d %d", &n, &m, &k) == 3; ) {
    initSets(n);
    D.clear();
    D.resize(k + 1);

    int d;
    Edge edge;
    long long result = 0;

    for (int i = 0; i < m; i++) {
      scanf("%d %d %d", &edge.x, &edge.y, &d);
      if (d <= k) {
        D[d].push_back(edge);
        continue;
      }
      int a = root(edge.x);
      int b = root(edge.y);
      if (cnt[a] > cnt[b])
        std::swap(a, b);
      // a -> b
      fa[a] = b;
      result -= c2(cnt[a]) + c2(cnt[b]);
      cnt[b] += cnt[a];
      result += c2(cnt[b]);
    }

    CDQ solve(k, result);
  }
  return 0;
}

  

Open_POJ C15C Rabbit's Festival

标签:

原文地址:http://www.cnblogs.com/gu-castle/p/5000120.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!