CF Div3 962補題 E-F

GsGXT發表於2024-07-29

CF Div3 962補題 E-F

E. Decode

連結:

Problem - E - Codeforces

簡要題意:

給你一個長度為 \(n\) 的二進位制字串\(s\) 。對於每一對整數\((l, r)\) \((1 \leq l \leq r \leq n)\) 中,數出 \((x, y)\) \((l \leq x \leq y \leq r)\) 這樣的整數對的個數。 \((l \leq x \leq y \leq r)\) 中的 \(\mathtt{0}\) 的數量等於子串 \(s_xs_{x+1}...s_y\).中的 \(\mathtt{1}\) 的數量。

輸出所有可能的 \((l, r)\) modulo \(10^9+7\) 的計數之和。

思路:

  • 很明顯這樣的題目算區間貢獻和字首和有關
  • 我們定義子串左邊位置為x 子串右邊位置設為y
  • 我們發現一個01數量相同的子串 對於整個陣列的貢獻是 \(x * (n-y+1)\)
  • 算01相同數量我們可以用字首和 + map實現
  • 每次將x + 1的貢獻累加到map中
  • 計算答案 ans = mp[pre[i]] * (n-i+1)

程式碼:

void solve(){
    string s;
    cin >> s;
    int n = s.size();
    s = " " +s;
    map<int,int> h;
    int pre = 0;
    int ans = 0;
    h[0] = 1;
    for(int i = 1;i<=n;i++){
        if(s[i]=='0') pre--;
        else pre++;
        ans+=h[pre]*(n-i+1)%P;
        ans%=P;
        h[pre]+=i+1;
        h[pre]%P;
    }
    cout << ans <<endl;

}

F. Bomb

連結:

Problem - F - Codeforces

簡要題意:

  • 給你兩個陣列 \(a[n]\) 和 $ b[n]$ \((1<= n <= 2e5)\)
  • 你可以執行最多k次下列操作 \((1<= k <= 1e9)\)
  • 選擇 \(a[i]\)\(a[i]\) 加入到你的分數中 然後使\(a[i] = max(0,a[i] - b[i])\)
  • 求最大分數

思路:

  • 首先一個O(k)的思路是 將所有a[i]放入優先佇列中,然後不斷取k個最大的優先佇列top即可,每次取出來都更新優先佇列的值 \(a[i] = max(0,a[i] - b[i])\) 再放入,因為可以取多次
  • k有1e9,顯然該思路不行
  • 這麼大的資料量 很自然的指向了二分,我們要二分什麼?
  • 二分一個x,此x為我每次取數字,至少要取大於等於x的數 (核心)
  • 我們得到取大於等於x的數會有一個操作次數,我們要使操作次數f(x) <= k, 並且取max(f(x));
  • 然後我們就會得到一個x,我們把每個數取的的貢獻不斷加入答案中,這步可用等差數列求和
  • 我們還有一部分是 (k - f(x)) 即剩餘操作次數,我們發現 如果x更小,那麼操作次數f(x) 會 > k,那麼我們只要取(k - f(x))個 x加入答案即可完成最後一部分的補充(這步很難理解,筆者建議多思考一下為什麼這樣補充)

程式碼:

const int N = 200005;
int a[N],b[N],n,k;
bool check(int mid){
    int y = 0;
    for(int i = 1;i<= n;i++){
        if(a[i]>=mid){
            y += (a[i] - mid + b[i] - 1)/b[i];
        }
    }
    return y <= k;
}
void solve(){
    cin >> n >> k;
    for(int i = 1;i<=n;i++){
        cin >>a[i];
    }
    for(int i = 1;i<=n;i++){
        cin >>b[i]; 
    }
    int l=0,r=1e12,x=-1;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(check(mid)){
            r = mid - 1;
            x = mid;
        }else{
            l = mid + 1;
        }
    }
    int us = 0;
    int ans = 0;
    for(int i = 1;i<=n;i++){
        if(a[i] >= x){
            int t = (a[i] - x + b[i] - 1)/b[i];
            //(a[i] + a[i] - b[i]*(t - 1))*t/2;
            ans += (a[i] + a[i] - b[i]*(t - 1))*t/2;
            us+=t;
        } 
    } 
    ans += (k - us)*x;
    cout << ans << endl;

}

signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    int times = 1;
    cin >> times;
    while(times--){
        solve();
    }
    return 0;
}