離散化的一道很經典的題

fafatadie發表於2024-10-13
  1. 區間和
    題目
    提交記錄
    討論
    題解
    影片講解

假定有一個無限長的數軸,數軸上每個座標上的數都是 0

現在,我們首先進行 n
次操作,每次操作將某一位置 x
上的數加 c

接下來,進行 m
次詢問,每個詢問包含兩個整數 l
和 r
,你需要求出在區間 [l,r]
之間的所有數的和。

輸入格式
第一行包含兩個整數 n
和 m

接下來 n
行,每行包含兩個整數 x
和 c

再接下來 m
行,每行包含兩個整數 l
和 r

輸出格式
共 m
行,每行輸出一個詢問中所求的區間內數字和。

資料範圍
−109≤x≤109
,
1≤n,m≤105
,
−109≤l≤r≤109
,
−10000≤c≤10000
輸入樣例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
輸出樣例:
8
0
5
離散化的含義便是對映,把有關的陣列下標對映為自然數0,1,2,3....或者1,2,3,4,5....,首先使用離散化的前提的條件便是值域跨度很大,但題目的輸入和輸出中牽扯到的數字下標沒有這麼大。例如本題:
值域範圍為:1e9的範圍,但本體在輸入時先給某個下標加值,n最多便是1e5,而m每次詢問有兩個下標(索引),m的範圍是1e5,故m * 2 + n 的範圍便是3e5 + 10(這個10是為了防止陣列越界的問題)。當然,如果資料範圍什麼時候變小了,大致是一個2*1e5的範圍,就可以用我們常用到的字首和來求解,這個離散化可以完美的使用我們的陣列,保證所有的下標都與題目有聯絡。
附上c++程式碼:

include

include

include

using namespace std;
const int N = 300010;
typedef pair<int,int> PII;

int n,m;
int a[N],b[N];

vectorstu;
vectoraoi,aio;

int find(int x)
{
int l = 0,r = stu.size() - 1;

while(l < r)
{
    int mid = l + r >> 1;
    
    if(stu[mid] >= x)  r = mid;
    else    l = mid + 1;
}

return r + 1;

}

int main()
{
scanf("%d%d", &n, &m);

for(int i = 0;i < n;i ++)
{
    int x,y;
    cin >> x >> y;
    
    stu.push_back(x);
    
    aoi.push_back({x,y});
}

for(int j = 0;j < m;j ++)
{
    int l,r;
    cin >> l >> r;
    
    stu.push_back(l);
    stu.push_back(r);
    
    aio.push_back({l,r});
}

sort(stu.begin(),stu.end());
stu.erase(unique(stu.begin(),stu.end()),stu.end());

for(auto boy:aoi)
    a[find(boy.first)] += boy.second;

for(int i = 1;i <= stu.size(); i ++)     b[i] = b[i-1] + a[i];

for(auto riu:aio)
    printf("%d\n" , b[find(riu.second)] - b[find(riu.first) - 1]);
    
return 0;

}

相關文章