1. 程式人生 > 實用技巧 >AcWing 秦騰與教學評估

AcWing 秦騰與教學評估

第一次在一個OJ上發了篇題解,沒想到他們沒有題解稽核機制,直接就出現在題解區了.

https://www.acwing.com/solution/content/27658/

那裡的markdown在這裡無效,我調了一下再發到這裡儲存一下.

二分
時間複雜度 O(nlogn)
藍書而來.
本題重點在於滿足條件的位置只會不存在或者有且僅有一個,而這個點有一個特殊性質:人數是奇數.
設想:在連續的一段位置上,如果每個位置上的人數加起來為偶數,那麼這一段裡面不可能存在一個奇數.
如果人數加起來為奇數,那麼唯一一個人數為奇數的位置必然在這一段上,利用這個特性可以二分求解.
注意對一段位置上的人數統計是可以寫出O(n)方法的,複雜度太高了會TLE.具體實現請結合程式碼註釋理解.
另外,不開long long見祖宗.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int T, n;
struct S{
    long long s, d, e;
}t[200010];

long long cnt(long long l, long long r){                // 統計區間[l, r]上的人數並返回
    long long ct = 0;
    long long begin, end;                               //
我們要統計的區間和輸入資料的每一個區間會出現包含,被包含,交集為空等各種情況,需要對這些情況判斷一下並找出等價的[begin, end]區間來處理,也就是說把原來的s,d,e轉化為等價的begin,d,end for(int i = 1; i <= n; i++){ // 對每一個區間依次處理 long long s = t[i].s, e = t[i].e, d = t[i].d; // 只是換一個方便的變數名 if(e < l || s > r) continue; //
區間沒有交集,跳過 if(l <= s) begin = s; // s在[l,r]之內,則以s為起始點 else { // s < l,那麼需要找到最小的k,使得s+k*d>=l,並以s+k*d為起始點 long long k = (l - s - 1) / d + 1; // 由s+k*d>=l得k>=(l-s)/d,稍微思考一下就發現我們需要對右式向上取整,想要對N/M向上取整,表示式為(N-1)/M +1 begin = s + k * d; } end = min(r, e); // 這裡對end的處理是顯然的 if(end - begin < 0) continue; ct += (end - begin) / d + 1; // 注意當end與bgin相等時人數一定是1 } return ct; } int main(){ scanf("%d", &T); while(T--){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%lld%lld%lld", &t[i].s, &t[i].e, &t[i].d); long long l = 0, r = 2147483648LL; // 注意有一個數據點剛好是int最大值,這裡再加一保險一下 bool bad = true; while(l < r && l >= 0){ long long mid = l + r >> 1; if(cnt(l, mid) & 1) r = mid, bad = false; else l = mid + 1; } if(bad) puts("Poor QIN Teng:("); else printf("%lld %lld\n", l, cnt(l, l)); } return 0; }
AC Code