Open_POJ C15C Rabbit's Festival

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

时间: 2024-10-03 13:47:47

Open_POJ C15C Rabbit's Festival的相关文章

【openjudge】C15C Rabbit&#39;s Festival CDQ分治+并查集

题目链接:http://poj.openjudge.cn/practice/C15C/ 题意:n 点 m 边 k 天.每条边在某一天会消失(仅仅那一天消失).问每一天有多少对点可以相互到达. 解法:开始不会做,参考的YYN的题解:http://blog.csdn.net/u013368721/article/details/45725181 学习了这种CDQ加并查集的做法,可以说是非常的巧妙了.复杂度可以保证在:O(KlogklogK)的范围. //CDQ + DSU #include <bit

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the

HDU4057 Rescue the Rabbit(AC自动机+状压DP)

题目大概是给几个DNA片段以及它们各自的权值,如果一个DNA包含某个片段那么它的价值就加上这个片段的权值,同时包含多个相同DNA片段也只加一次,问长度l的DNA可能的最大价值. 与HDU2825大同小异. dp[i][j][S]表示长度i(自动机转移i步).后缀状态为自动机第j个结点.包含的DNA片段为集合S 的DNA最大价值 dp[0][0][0]=0 我为人人转移,从dp[i][j][S]向ATCG四个方向更新dp[i+1][j'][S'] 注意的是要用滚动数组,不然要开上百兆数组. 用滚动

HDU1849 Rabbit and Grass()

用异或看取得的值是否为0判断 思想换没搞懂 #include<stdio.h> int main() { int ans,n,a; while(scanf("%d",&n),n){ ans=0; while(n--){ scanf("%d",&a); ans=ans^a; } if(ans==0) printf("Grass Win!\n"); else printf("Rabbit Win!\n"

HDU 4057 Rescue the Rabbit (AC自动机+DP)

http://acm.hdu.edu.cn/showproblem.php?pid=4057 Rescue the Rabbit Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1482    Accepted Submission(s): 430 Problem Description Dr. X is a biologist,

hdu 1850 Being a Good Boy in Spring Festival (尼姆博弈)

Being a Good Boy in Spring Festival Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4658    Accepted Submission(s): 2781 Problem Description 一年在外 父母时刻牵挂春节回家 你能做几天好孩子吗寒假里尝试做做下面的事情吧 陪妈妈逛一次菜场悄悄给爸爸买

HDU - 1849 - Rabbit and Grass

先上题目: Rabbit and Grass Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2097    Accepted Submission(s): 1579 Problem Description 大学时光是浪漫的,女生是浪漫的,圣诞更是浪漫的,但是Rabbit和Grass这两个大学女生在今年的圣诞节却表现得一点都不浪漫:不去逛

HDU - 1850 - Being a Good Boy in Spring Festival

先上题目: Being a Good Boy in Spring Festival Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4557    Accepted Submission(s): 2713 Problem Description 一年在外 父母时刻牵挂春节回家 你能做几天好孩子吗寒假里尝试做做下面的事情吧 陪妈妈逛一次菜场

13杭州区域赛现场赛Rabbit Kingdom(树状数组+离线)

题意:给你一个长度数列,再给你m个询问(一个区间),问你在这个区间里面有多少个数与其他的数都互质. 解题思路:你看这种类型的题目都可以肯定这是 离线+树状数组(线段树).主要就是他的更新信息.这里我的处理是先把1-200000(每个数的范围)数里面所有的质因子求出来.然后从后往前遍历数组.会出现以下几种情况 1.a[k]的质因子在后面出现过而[更新标记] 和[被更新标记] 都为假 2.a[k]的质因子在后面出现过的那个位置 I   [更新标记]为 真 . 3.a[k]的质因子在后面出现过且那个位