전공/알고리즘

백준 19588 상품권 준비

xkdlaldfjtnl 2022. 4. 3. 18:35

https://www.acmicpc.net/problem/19588

 

19588번: 상품권 준비

“4”가 출제진이 되어야 하며, [“2”, "7”], [“6”, “8”] 로 팀을 구성하는 것이 (10 × 20 + 30 × 40)으로 최강의 팀 구성이다. 따라서 총 (2 ⊕ 7) + (6 ⊕ 8) = 19가 준비해야 할 총액이다.

www.acmicpc.net

xor은 누적합 테크닉 이용이 가능하다. 

a^b^a = b이므로

이 사실을 이문제 풀면서 처음 알았음. 

 

 

N,M의 제한이 10^5, 쿼리가 5*10^5 이므로 만약 쿼리마다 계산한다면 무조건 시간초과가 날 수 밖에 없다는 사실은 누구나 알 수 있다고 생각 

 

따라서 전처리를 어떻게 하느냐가 중요한 문제로 바뀌는데

 

문제에서 상위 b명은 제외되니깐 위에서 부터 어떻게 뽑는지 모양이 달라진다. 

그러니깐 

 

1

2

3

4

5

 

5명이 존재하고, 2명씩 그룹을 만들때 0명이 빠지면 (5,4),(3,2) 1명이 빠지면 (4,3), (2,1) 이런식으로 달라지게 되는데

이 모양은 총 M개 존재한다.

 

index 0부터 시작, 1부터 시작, ... M-1 부터 시작 

 

그리고 모든 M에 대해서 몇개를 그룹으로 이루어진건지 구해놓는다. 

이 개수는 총 N/M개이다. 

 

따라서 전처리에 O(N/M * M + N)의 시간복잡도가 필요하다.

 

쿼리문에서 받은 a,b로 잘 index계산해서 원하는 걸 출력하면 된다.

 

#include <bits/stdc++.h>
using namespace std;
#define MAX 200005
#define pii pair<int, int>
#define ll long long
#define pll pair<ll, ll>
#define INF 1e9
#define LINF 1e18
#define all(v) (v).begin(), (v).end()

vector<pll> num;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while(T--) {
        ll N, M;
        cin >> N >> M;
        for(int i=0; i<N; i++){
            ll a, b;
            cin >> a >> b;
            num.emplace_back(a,b);
        }
        sort(all(num));
        vector<ll> sum(N+1);
        for(int i=1; i<=N; i++){
            sum[i] = sum[i-1]^num[i-1].second;
        }
        vector<vector<ll> > presum(N/M+1,vector<ll>(M,0));
        for(int i=0; i<M; i++){
            for(int j=1; j<=N/M; j++){
                presum[j][i] = (sum[i+j*M]^sum[i+j*M-M]) + presum[j-1][i];
            }
        }
        int Q;
        cin >> Q;
        for(int i=0; i<Q; i++){
            ll a, b;
            cin >> a >> b;
            ll k = N-b-a*M;
            k %= M;
            cout << presum[(N-b)/M][k] - presum[(N-b-a*M)/M][k] << '\n';
        }
    }
}