【问题描述】
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);
}