接頭和と差分配列の応用

接頭和 & 差分配列

  • 計算量最適化のための基本技術
  • 接頭和は複数回の区間クエリを高速化する手法。配列arr[N]に対してpre_sum[N]を構築し、

pre_sum[i] = pre_sum[i-1] + arr[i]と定義する。インデックスは1から始める必要がある。

実践問題

N都市を結ぶ道路があり、各セグメントの移動コストが与えられる。伝送装置を使って最大kセグメント飛躍可能。ただし1回のみ使用可能。

ll solve() {
    ll n, k;
    cin >> n >> k;
    vector<ll> cost(n+1), pre_sum(n+1);
    for(ll i=1; i<=n; i++) {
        cin >> cost[i];
        pre_sum[i] = pre_sum[i-1] + cost[i];
    }
    
    if(k >= n) return 0;
    
    ll max_jump = 0;
    for(ll i=k; i<=n; i++) {
        max_jump = max(max_jump, pre_sum[i] - pre_sum[i-k]);
    }
    
    return pre_sum[n] - max_jump;
}

差分配列

  • 接頭和の逆操作
  • 区間更新をO(1)で処理可能

差分配列diff[N]を構築し、区間[l,r]にxを加算する操作は:

void range_add(int l, int r, int x) {
    diff[l] += x;
    diff[r+1] -= x;
}

応用例

鉄道区間の利用回数カウント問題。各区間で紙の切符かICカードのどちらがお得かを判定。

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> path(m+1), diff(n+2, 0);
    
    for(int i=1; i<=m; i++) cin >> path[i];
    
    for(int i=1; i> a >> b >> c;
        res += min(diff[i]*a, diff[i]*b + c);
    }
    
    cout << res << endl;
}

二次元接頭和 & 差分

二次元配列の累積和計算公式:

prefix[i][j] = prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1] + grid[i][j]

矩形領域の合計計算:

sum = prefix[x2][y2] - prefix[x1-1][y2] - prefix[x2][y1-1] + prefix[x1-1][y1-1]

土地選定問題

void calc_prefix() {
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            prefix[i][j] = prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1] + grid[i][j];
        }
    }
}

int main() {
    int n, m, c;
    cin >> n >> m >> c;
    
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            cin >> grid[i][j];
        }
    }
    
    calc_prefix();
    
    int max_val = -INF;
    for(int i=1; i<=n-c+1; i++) {
        for(int j=1; j<=m-c+1; j++) {
            int x1=i, y1=j, x2=i+c-1, y2=j+c-1;
            int val = prefix[x2][y2] - prefix[x1-1][y2] - prefix[x2][y1-1] + prefix[x1-1][y1-1];
            if(val > max_val) {
                max_val = val;
                ans_x = i, ans_y = j;
            }
        }
    }
    
    cout << ans_x << " " << ans_y << endl;
}

タグ: 接頭和 差分配列 累積和 区間更新 二次元配列

7月24日 18:21 投稿