Frets On Fire --- 2019 Codeforces Global Round 2 Problem D
原題:https://codeforces.com/contest/1119/problem/D
題意大概是一個n行1e18列的矩陣,其中每行第一個數為s[i],剩下的數每行依次以1的速度遞增。就是說,矩陣元素 a[i][j] = s[i] + j 。有q個詢問,每個詢問有兩個參數l,r,求矩陣第l列到第r列(所有行)一共出現了幾個不同的數。
這道題首先要先想到,兩個參數 [l,r] 其實等價於一個參數 [0,r-l] ,也就是說矩陣第0~r-l行出現的數字的個數其實和第l-r行出現的個數是一樣的。我們可以這樣理解:[l,r] 和 [0,r-l] 代表的區域本質上是兩個矩形,而他們的位置關系就是平移而已,如下圖兩個矩陣,他們的大小一致,因此其中元素有一一對應的關系,也就是每個數都減去(向左平移)了 l 個單位。而整體的數字出現的個數是不變的。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... | |
0 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... |
1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... |
2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ... |
3 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ... |
5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ... |
<--向左平移L個單位<--
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... | |
0 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... |
1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... |
2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ... |
3 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ... |
5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ... |
現在,我們可以把每一行看作一個 [s[i],s[i]+r-l] 的閉區間,於是我們把這n個閉區間放到數軸上,如果沒有重合,那麽答案就是n*(r-l+1)了,但顯然區間重合是可能存在的。
這時我們觀察數軸上的這n個閉區間,發現他們無論何時(每組詢問)的長度都是一致的,因此我們可以把這些區間分成兩類:如果對於一個區間I = [xi,yi], 存在區間J = [xj,yj], 使得xj <= yi,我們說區間I是”貢獻確定“的,因為由於區間長度一致,區間I在yi之後的數字都可以由區間J所替代,所以此時區間I的貢獻確定為yi-xi。相反地,如果不存在這樣的區間J,那麽區間I就是”貢獻不確定“的,他的貢獻與區間長度有關(其實就是長度+1,閉區間嘛)。
(劣質示意圖)
回到這道題,每個詢問等價的那個參數(記作q[k], q[k] = rk-lk),本質上就是那個區間長度,所以我們知道對於每個”貢獻不確定“的區間,他的貢獻是q[k] + 1。那對於”貢獻確定“的區間。。。廢話他們的貢獻都確定了好嗎。那這個確定的貢獻是多少呢,我們可以把這n個區間排個序,排好序之後第i個區間的確定貢獻(也就是最大的貢獻,記作t[i])就是 t[i] = s[i+1] - s[i] , 也就是他和下一個區間開頭的距離了。最後,判斷一個區間是否確定貢獻的方式就是判斷t[i] 與 q[k] 的大小關系,如果t[i] <= q[k], 那麽他的區間尾就碰到了下一個區間頭,貢獻就確定為t[i]了。我們可以將 t[i] 排序,二分查找有多少個區間為確定貢獻的。
那麽,對於每一個詢問 q[k], 覆蓋的數字的個數 ans[k] = Σpi=1t[i] + (n-p) * (rk-lk+1), 其中p代表有且僅有前p小的t[i] 小於q[k]。需要前綴和數組sum[p]記錄前p個t[i] 的和。
代碼如下:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 using namespace std; 5 typedef unsigned long long ull; 6 const int maxN = 1e5 + 3; 7 const ull inf = 3e18; 8 ull n, s[maxN] = {0}, qn, q[maxN] = {0}, t[maxN] = {0}, sum[maxN] = {0}; 9 int main() 10 { 11 ull temp; 12 ios::sync_with_stdio(false); 13 cin >> n; 14 for(int i = 1; i <= n; i++) 15 cin >> s[i]; 16 cin >> qn; 17 for(int i = 1; i <= qn; i++) 18 { 19 cin >> temp >> q[i]; 20 q[i] -= temp; 21 } //讀入,詢問參數二變一 22 23 sort(s+1,s+1+n); 24 t[0] = 0; 25 for(int i = 1; i < n; i++) 26 t[i] = s[i+1] - s[i]; 27 t[n] = inf; // 最後一個區間永遠也不會與下一個區間重合,貢獻是不確定的 28 sort(t,t+n); // sort記得寫在前綴和之前。。。 29 for(int i = 1; i < n; i++) 30 sum[i] = sum[i-1] + t[i]; 31 32 for(int i = 1; i <= qn; i++) 33 { 34 int l = 0, r = n-1, mid; // q可能小於所有的t,但不可能大於所有的t 35 while(l < r) 36 { 37 mid = (l+r+1) >> 1; // 記得+1,防止死循環 38 if(t[mid] <= q[i]) 39 l = mid; 40 else 41 r = mid - 1; 42 } 43 cout << sum[l] + (q[i]+1) * (n-l) << ‘ ‘; 44 } 45 cout << endl; 46 47 return 0; 48 }
Frets On Fire --- 2019 Codeforces Global Round 2 Problem D