題目傳送門『p1083 借教室』
資料範圍:\(1\leq n,m\leq10^6\)
題目意思:給定長度為\(n\)的陣列,和\(m\)次詢問,每次詢問把陣列\([l,r]\)減去\(d\),問第幾次詢問能夠使得陣列能夠存在小於0的數。
樸素做法:對當前\(1..n\)對每個詢問區間做修改(閉區間\([l,r]\)同時減去\(d\)),同時掃瞄當前區間長度\([1,n]\)是否出現\(a[i]\leq0\)。
不難分析時間複雜度是\(o(nm)\),而\((10^6)^2>>10^7\)必然超時。觀察資料\(1e6\)的資料考慮\(nlgn\)做法。
時間複雜度\(o(\text(m)*(n+m))\)
本題關鍵是如果第\(k\)個答案是不符合要求的,那麼後面的都肯定不符合要求(後面減去上一些數,肯定也是小於0的)。即答案具有二段性。我們需要找到乙個臨界點,在這個臨界點區間某個點下一次會被減到負數,而該點後面都是不滿足的情況。
因此,我們需要實現乙個函式check(m)
,使得詢問當前這個點\(m\)是符合的(如果區間每乙個數都是小於原來陣列的那麼是符合的),並且通過二分找到這個符合點的最右端點\(k\)。
再次發現,每一次再修改乙個區間都是同時加上或者減去某個數,最後會詢問當前這個答案m下,是否是符合的,屬於離線修改區間,可以考慮使用差分思想。
方法是對於 \([l,r]\) 加上\(d\)實質上是對於差分陣列\(p\)做\(p[l]+d\),\(p[r+1]-d\)。
即為:
for (int i = l; i <= r; i++)
最後對差分陣列q求字首和:
for (int i = 1; i <= n; i++) q[i] = q[i] + q[i - 1];
這樣即可在\(o(n+m)\)的時間複雜度內得到修改後的陣列。
而暴力是需要\(o(m*n)\)的時間複雜度。
因為總共有\(m\)次詢問,二分\([1,m]\)區間總共是\(o(\text(m))\)次
而每一次是使用差分優化,差分時間複雜度是\(o(n+m)\),因此時間複雜度是\(o(\text(m)*(n+m))\),
綜上比暴力\(o(nm)\)快得多。
//
// author: oceanlvr
#include using namespace std;
typedef long long ll;
static int faster_iostream = () ();
int n, m;
const int maxn = 1e6 + 10;
ll a[maxn];
ll q[maxn];
ll d[maxn], s[maxn], t[maxn];
bool check(int idx)
//對差分做字首和 得到最後m次修改之後的陣列
for (int i = 1; i <= n; i++) q[i] = q[i] + q[i - 1];
for (int i = 1; i <= n; i++)
} return true; //滿足分配條件
}int main() else
} if (r == m) else if (check(r)) else
return 0;
}
洛谷 P1083 借教室 二分 差分
題意 思路 首先可以看出這是乙個比較典型的差分,首先是離線,然後在區間l,r增加幾,這些都是差分的特徵,那麼現在問題就是,怎樣差分?我們可以看出天數是具有單調性的,當在第x天不行的時候那麼x 1肯定也不行,由此可以看出天數是具有二分的特性的,所以我們可以二分天數。include using name...
洛谷P1083 借教室 二分 差分
n天,每天有r i 個教室出租,一共m個訂單,按照先後順序,每個訂單從s天開始到t天結束,每天要借d個,如果所有訂單都能完成,輸出0,哪天教室不夠,輸出到那個訂單的單號。二分可以完成到第幾個訂單,對於每個二分的值,進行差分到這個訂單為止所有的天數,求下字首和,就可以得出對於每一天有沒有超出借出的教室...
洛谷 P1083 借教室(二分 差分)
題目大意 有數列an,每次我們可以選擇乙個區間做減法,問我們第幾次操作會導致乙個區間出現負數。n 1e6 解題思路 線段樹是可以做的,但是貌似這題卡了線段樹。這裡使用二分列舉答案的方法。為什麼這裡適合用差分呢?因為這裡滿足false false true false的結構。每次列舉到乙個點時,我們對...