1. 程式人生 > >Frets On Fire --- 2019 Codeforces Global Round 2 Problem D

Frets On Fire --- 2019 Codeforces Global Round 2 Problem D

裏的 區間 然而 clas 數字 r+ 其中 前綴 二分查找

原題: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)了,但顯然區間重合是可能存在的。

最暴力的想法就是初始化一個mark[值域]數組,對每一個區間裏的數字逐個mark上,最後再遍歷值域。然而這玩意兒一次詢問就是O(n*值域),顯然不行。

  這時我們觀察數軸上的這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