hdu5808 整體二分

題目描述

在Byteland一共有nn家商店,編號依次爲11到nn。每家商店只會賣一種物品,其中第ii家商店的物品單價爲vi ,且它到Byteasar的家的距離爲di

Byteasar每天都會進行一次購物,第ii天他會選擇一個區間[li ,ri ],並給自己設定一個距離上限ci ,然後他會在編號在該區間內每家到自己家的距離不超過ci 的商店購買最多一件物品,當然他也可以選擇什麼都不買。回家之後,Byteasar會把今天購物所花的錢的總數sumi 記錄在賬本上。

Byteasar的數學不好,他可能會把花的錢記錯。

請寫一個程序,幫助Byteasar判斷每條記錄是否一定是錯的。

注意:記多或者記少都算記錯。

解法

離線處理查詢,使用整體二分。

官方題解寫的比較清楚:

考慮對序列進行分治,設當前分治區間爲[l,r],取mid=(l+r)/2,那麼所有在[l,mid)的詢問和(mid,r]的詢問可以遞歸分治求解,故只需考慮必然經過mid的詢問。

設f[i][j]表示考慮了[i,mid],目前選出的物品和爲j時,所選商店到家的距離的最大值最小是多少;g[i][j]表示考慮了(mid,i],目前選出的物品和爲j時,所選商店到家的距離的最大值最小是多少,f和g都能在O(100(r-l+1))的複雜度內求出。那麼對於一個詢問l,r,s,cl,r,s,c,只需要求出min(max(fi,gsi)) ,然後和c比較一下大小就好了。

時間複雜度O(100(nlogn+m)+mlogn)

在這裏f和g可以合併到一個數組裏面

#include<bits/stdc++.h>
using namespace std;

const int QSIZE = 100005;
const int SIZE = 20005;

int n, m;
struct node {
    int L, R, i, c, sum;
    void read(int _i) {
        i = _i;
        scanf("%d%d%d%d",&L,&R,&c,&sum);
    }
} q[QSIZE], temp[QSIZE];
int res[QSIZE];

int v[SIZE];
int d[SIZE];
int f[SIZE][105];

const int INF = 0x7fffffff;

void updatef(int l, int r) {
    for(int i = r; i >= l; i--) {
        for(int j = 0; j <= 100; j++) {
            if(j - v[i]>= 0) f[i][j] = min(f[i+1][j], max(f[i+1][j-v[i]], d[i]));
            else f[i][j] = f[i+1][j];
        }
    }
}

void updateg(int l, int r) {
    for(int i = l; i <= r; i++) {
        for(int j = 0; j <= 100; j++) {
            if(j - v[i] >= 0) f[i][j] = min(f[i-1][j], max(f[i-1][j-v[i]], d[i]));
            else f[i][j] = f[i-1][j];
        }
    }
}

void solve(int l, int r, int ql, int qr) {
    if(ql > qr) return;
    if(l == r) {
        for(int i = ql; i <= qr; i++) {
            res[q[i].i] = v[r] == q[i].sum && d[r] <= q[i].c;
        }
        return;
    }
    int mid = (l + r) >> 1, w1 = ql, w2 = qr, w3 = 0; //三組不同查詢的遊標 
    for(int i = ql; i <= qr; i++)
    {  
        if (q[i].R <= mid) q[w1++] = q[i];  
        else if (q[i].L > mid) temp[w2--] = q[i];  
        else temp[w3++] = q[i];  
    }
    for(int i = w2 + 1; i <= qr; i++) q[i] = temp[i];

    if(w3) {
        for(int i = 1; i <= 100; i++) f[mid][i] = f[mid + 1][i] = INF;
        f[mid][0] = f[mid+1][0] = 0;
        f[mid][v[mid]] = d[mid];
        f[mid+1][v[mid+1]] = d[mid+1];
        updatef(l, mid-1);
        updateg(mid+2, r);
    }
    for(int i = 0; i < w3; i++) {
        for(int j = 0; j <= temp[i].sum; j++) 
            res[temp[i].i] |= max(f[temp[i].L][j], f[temp[i].R][temp[i].sum - j]) <= temp[i].c;
    }
    solve(l, mid, ql, w1 - 1);
    solve(mid + 1, r, w2 + 1, qr);
}

int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++) scanf("%d",&v[i]);
        for(int i = 1; i <= n; i++) scanf("%d",&d[i]);
        for(int i = 1; i <= m; i++) q[i].read(i);
        memset(res, 0, sizeof(res));
        solve(1, n, 1, m);
        for(int i = 1; i <= m; i++) printf("%d", res[i] ^ 1);
        puts("");
    }
    //scanf("%d",&n);
    return 0;
}

整體二分查詢區間分割

自己寫的比較挫,借鑑了別人的寫法,學到了。

int mid = (l + r) >> 1, w1 = ql, w2 = qr, w3 = 0; //三組不同查詢的遊標 
    for(int i = ql; i <= qr; i++)
    {  
        if (q[i].R <= mid) q[w1++] = q[i];  
        else if (q[i].L > mid) temp[w2--] = q[i];  
        else temp[w3++] = q[i];  
    }
    for(int i = w2 + 1; i <= qr; i++) q[i] = temp[i];
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章