【並查集】淩辱

来源:互联网 发布:2016年淘宝双11销售额 编辑:程序博客网 时间:2024/06/06 12:36

【问题描述】  13 级的男生太菜了,于是神母牛小希准备在省选的前一天晚上在宾馆凌辱他们,男生们由于众不敌寡只有乖乖听话。宾馆有 n 个房间,她把男生们随机安排进宾馆的一些房间。由于小希记忆力有限,她仅仅记得从第a号到第b号房间一共关了多少个男生。现在小希把她知道的所有信息都告诉你,然后询问你从第c号到第d号房间一共关了多少男生,如果无法得出确切答案,请回答“hehe”来卖萌(引号无需输出)。记住,凌辱与被凌辱,小希就在那里。 【输入数据】第一行三个整数 n,m,k,表示共有 n 个房间,m条信息和 k次询问。接下来 m行每行3 个整数a b c,表示区间[a,b]的房间关了 c个男生。接下来 k 行每行 2个整数a b,表示询问区间[a,b]的房间关了多少个男生。 【输出数据】共 k 行,每行表示一次询问的结果。 【样例输入】                                                 10 4 31 3 52 3 35 8 66 7 31 16 72 4【样例输出】23hehe【样例解释】红色区域代表小希的信息,绿色区域表示通过计算能算出的区域。 
 

【数据范围及约定】1≤n≤3000,0≤m≤10000,0≤k≤10000。输入数据保证正确性,无需验证。答案和中间值都在 longint范围内

先對題目內容表示無語。。。

再說解題方法:

這是一個並查集的題。


題目並不困難,但很容易走入一個誤區就是線段樹。
當然題目很像線段,即若干個對區間的操作和查詢,但由於題目中的區間和其子區間並沒有嚴格的條件包含關係,所以不能用線段樹解決。

做法很簡單實則相當於一個一維求點的距離問題。
使用並查集,並維護每個節點到根的距離即d值。
易証:當且僅當詢問的兩個節點在同一個集合當中時,才能得出結果。若能得出,則為兩端點到它們共同的根的距離之差。

這樣就簡化了問題!

Accode:

#include <cstdio>#include <cstdlib>#include <bitset>const char fi[] = "insult.in";const char fo[] = "insult.out";const int maxN = 3010;const int maxM = 10010;int F[maxN], d[maxN];int n, m, k, L, R, c;  void init_file()  {    freopen(fi, "r", stdin);    freopen(fo, "w", stdout);  }  inline int Find(int x)  {    if (F[x] == x) return x;    int tmp = F[x];    F[x] = Find(F[x]);//注意這裡必須先找到根才能更新d值。    d[x] += d[tmp];    return F[x];  }  void work()  {    scanf("%d%d%d", &n, &m, &k);    for (int i = 1; i < n + 1; ++i)      F[i] = i;    for (; m; --m)    {      scanf("%d%d%d", &L, &R, &c);      int l = Find(--L), r = Find(R);      if (l != r)        {F[l] = r; d[l] = c - d[L] + d[R]; }    }    for (; k; --k)    {      scanf("%d%d", &L, &R);      if (Find(--L) != Find(R)) printf("hehe\n");      else printf("%d\n", d[L] - d[R]);    }  }int main(){  init_file();  work();  exit(0);}
原创粉丝点击