HDU - 6274 Master of Sequence (二分 + 公式推導)

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6274

題目大意:給出一個長度爲n的數組a和數組b,定義函數S(t)=\sum_{i = 1}^{n}\left \lfloor \frac{t-b[i]}{a[i]} \right \rfloor

接下來有m次操作,操作有如下三種:

1 x y :令 a[x] = y

2 x y :令b[x] = y

3 k : 找出最小的 t ,使其滿足S(t)>=k,並輸出t。

題目思路:要解決這個題目,得抓住題目中給出的兩個關鍵條件:

1、對於任意的 a[i] 均小於等於1000 ;2、操作3的數量小於等於1000。

對於原式子S(t)=\sum_{i = 1}^{n}\left \lfloor \frac{t-b[i]}{a[i]} \right \rfloor,我們可以令t=k_{1}*a[i]+c_{1}b[i]=k_{2}*a[i]+c_{2},則原式子對於第i項可以化爲,

\left \lfloor \frac{k_{1}*a[i]+c_{1}-(k_{2}*a[i]+c_{2})}{a[i]}\right \rfloor,即(k_{1}-k_{2} + \frac{c_{1}-c_{2}}{a[i]})

當c1 >= c2 時,第 i 項的值爲k1 - k2,

當c1 < c2 時,第i 項的值爲k1 - k2-1

所以S(t)=\sum_{i=1}^{n}(k_{1i}-k_{2i}+(c_{1i}<c_{2i})

由於是要求最小滿足條件的 t ,我們自然可以考慮用二分來解決了。

由於 b[i] 的值和 a[i] 的值是固定的,所以我們可以通過預處理來求出\sum_{i=1}^{n}k_{2i}的值,移項一下,就變成了求滿足

\sum_{i=1}^{n}(k_{1i}-(c_{1i}<c_{2i}))>=k+\sum_{i=1}^{n}k_{2i}的 t 了。

在二分check的時候,由於 t 已經是已知的了,那麼k_{1i}也是固定的了,我們現在只需要考慮那些c_{1i}<c_{2i}的情況,減掉這些情況就行了。

由於a[i]<=1000,我們就可以用數組 cnt[i][j] 表示a[x] == i 同時 b[x]%a[x] >= j的個數有多少個,cnt數組可以直接預處理處理。

這樣對於每個c_{1i},我們只用cnt數組就可以找出有多少個c_{2i}>c_{1i}。再將這些情況減去即可。

具體實現看代碼:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
typedef pair<double, int> pdi;
const int mod = 998244353;
const int MX = 1e5 + 7;
const int inf = 0x3f3f3f3f;

int n, m, T;
int a[MX], b[MX];
ll cnt[1005][1005];

bool check(ll t, ll x) {
    ll res = 0;
    for (int i = 1; i <= 1000; i++) {
        res += t / i * cnt[i][0];
        res -= cnt[i][t % i + 1];
    }
    return res >= x;
}

ll solve(ll x) {
    ll l = 1, r = 1e13;
    ll ans = 0;
    while (l <= r) {
        ll mid = (l + r) >> 1;
        if (check(mid, x)) {
            ans = mid;
            r = mid - 1;
        }
        else l = mid + 1;
    }
    return ans;
}

int main() {
    // FIN;
    cin >> T;
    while (T--) {
        scanf("%d%d", &n, &m);
        clr(cnt);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        ll res = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &b[i]);
            res += b[i] / a[i];
            cnt[a[i]][b[i] % a[i]]++;
        }
        for (int i = 1; i <= 1000; i++) {
            for (int j = i - 1; j >= 0; j--)
                cnt[i][j] += cnt[i][j + 1];
        }
        int op, x, y, k;
        while (m--) {
            scanf("%d", &op);
            if (op == 1) {
                scanf("%d%d", &x, &y);
                for (int i = b[x] % a[x]; i >= 0; i--)
                    cnt[a[x]][i]--;
                for (int i = b[x] % y; i >= 0; i--)
                    cnt[y][i]++;
                res -= b[x] / a[x]; res += b[x] / y;
                a[x] = y;
            } else if (op == 2) {
                scanf("%d%d", &x, &y);
                for (int i = b[x] % a[x]; i >= 0; i--)
                    cnt[a[x]][i]--;
                for (int i = y % a[x]; i >= 0; i--)
                    cnt[a[x]][i]++;
                res -= b[x] / a[x]; res += y / a[x];
                b[x] = y;
            } else {
                scanf("%d", &k);
                printf("%lld\n", solve((ll)res + k));
            }
        }
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章