標籤:blog 檔案 資料 問題 for io
題目描述
佳佳是個貪玩的孩子。一天,他正在跟凡凡玩“數石子”的遊戲。佳佳在地上擺了N堆石子,其中第I堆石子有Ai個石頭。佳佳每次都會問凡凡:“凡凡,請問從第I堆到第J堆,總共有多少個石子?”聰明的凡凡每次都能快速而準確地回答對。凡凡老是被問問題,心裡有些不服,就對佳佳說:“佳佳,你還記得你問了什麼問題,我回答了什麼答案嗎?”佳佳說當然記得。於是凡凡說:“好,我把石子拿走,再問你一些相似的問題,你能答得出來嗎?”佳佳張圓了嘴巴,望著凡凡,一臉疑問和驚訝的表情。你現在知道了遊戲規則和過程,但沒看見原來的石子。請你寫一個程式來協助佳佳。
資料範圍
10%的資料滿足1<=n<=10,0<=m,k<=10
30%的資料滿足1<=n<=500,0<=m,k<=500
100%的資料滿足1<=n<=5000,0<=m,k<=10000
輸入格式
輸入檔案的第一行有3個數N(1<=n<=5000),M,K(0<=m,k<=10000),表示N堆石子,佳佳問了M個問題,凡凡要問K個問題。接下來M行每行3個整數L,R(1<=l<=r<=n),X(-108<=X<=108),表示佳佳問從L堆到R堆的石子共有多少個,而凡凡回答X個。接下來K行每行2個整數A,B(1<=a<=b<=n),每行表示凡凡問從A到B這些堆裡的石子有多少個。
輸出格式
輸出檔案需要對於凡凡提出每一個提問,你若可以回答,則輸出答案,若無法回答,輸出UNKNOWN。
範例輸入 10 5 51 5 42 5 43 6 51 9 96 6 21 92 61 23 51 7範例輸出 9613UNKNOWN 一開始想著把已知的所有線段的兩端的數字看成是節點,石子數看成是線段的權值,每次都把大的這一端掛到小的這一端上。但是這樣會出現一個問題,已知1-5,2-5,我們可以知道1-1的值,但是僅僅通過歸併之後的樹並沒有辦法直接算出來,在這裡可以採取一些處理方式。把所有的區間變成是左開右閉的區間,這樣子的話1-5,2-5就變成了0-5,1-5,合并之後變成了0為根節點的樹有兩個兒子1和5,這樣我們就可以查詢1-1和2-5,通過查詢同樣的左開右閉區間0-1,1-5即可至于歸並的過程就是簡單的帶權值並查集的歸併,無腦寫即可
#include <cstdio>#include <cstring>using namespace std;const int maxn = 50000 + 5;int fa[maxn],dist[maxn],N,M,K;int f(int a) { if(fa[a] == a) return a; int root = f(fa[a]); dist[a] = dist[a] + dist[fa[a]]; fa[a] = root; return root;}void uni(int a,int b,int d) { a = a - 1; int ya = f(a),yb = f(b); if(ya == yb) return; fa[yb] = a; dist[yb] = d - dist[b];}int main() { scanf("%d%d%d",&N,&M,&K); for(int i = 1;i <= N;i++) { fa[i] = i; } for(int i = 1;i <= M;i++) {int a,b,d; scanf("%d%d%d",&a,&b,&d);uni(a,b,d); } for(int i = 1;i <= K;i++) { int a,b; scanf("%d%d",&a,&b); a--; int ya = f(a),yb = f(b); if(ya != yb) puts("UNKNOWN"); else { printf("%d\n",dist[b] - dist[a]); } } return 0;}