【並查集】淩辱

【问题描述】
  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 3
1 3 5
2 3 3
5 8 6
6 7 3
1 1
6 7
2 4

【样例输出】
2
3
hehe

【样例解释】
红色区域代表小希的信息,绿色区域表示通过计算能算出的区域。 
 

【数据范围及约定】
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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值