atc abc465F 思路分享(高维前缀和及查询)
https://atcoder.jp/contests/abc465/tasks/abc465_f
题意
有 \(n\) 件物品,每个物品有:
-
唯一的 \(6\) 位数字编号 \(S_i\).
-
大小 \(V_i\).
有 \(q\) 个询问,每次询问给定两个 \(6\) 位编号 \(X,Y\),求满足:
\[X_j \le S_{i,j} \le Y_j
\]
的 \(V_i\) 之和.
\(1\le n,q \le 3\times 10^5\).
思路
每一位都有上下界限制的区间查询,这是高维前缀和问题.
构建前缀和部分只需要对每一维单独做前缀和即可,查询部分需要考虑容斥.
回忆二维前缀和的区间查询:
\[pre[x_2][y_2]-pre[x_1-1][y_2]-pre[x_2][y_1-1]+pre[x_1-1][y_1-1]
\]
六维是类似的,对 \(2^6\) 个角作容斥,取 \(p_1-1\) 的维数为奇则减,否则加.
时间复杂度 \(\mathcal{O}(N\cdot 10^N+Nq\cdot 2^N)\),\(N\) 是维数.
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int fac[7];
void init(){
fac[0] = 1;
for (int i=1;i<=6;i++){
fac[i] = fac[i-1]*10;
}
}
int id(string& s){
int res = 0;
for (int i=0;i<6;i++){
res += fac[i]*(s[i]-'0');
}
return res;
}
void solve(){
int n;
cin >> n;
vector<ll> pre(fac[6]);
for (int i=1;i<=n;i++){
string s;
int v;
cin >> s >> v;
pre[id(s)] += v;
}
for (int i=0;i<6;i++){
for (int u=0;u<fac[6];u++){
if (u/fac[i]%10){
pre[u] += pre[u-fac[i]];
}
}
}
int q;
cin >> q;
while (q--){
string s,t;
cin >> s >> t;
bool ok = true;
for (int i=0;i<6;i++){
if (s[i]>t[i]){
ok = false;
break;
}
}
if (!ok){
cout << 0 << '\n';
continue;
}
ll res = 0;
for (int u=0;u<1<<6;u++){
string cur;
bool f = true;
for (int i=0;i<6;i++){
if (u>>i&1){
cur += t[i];
}
else{
if (s[i]=='0'){
f = false;
break;
}
cur += s[i]-1;
}
}
if (f){
res += ((6-__builtin_popcount(u))&1?-1:1)*pre[id(cur)];
}
}
cout << res << '\n';
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
init();
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号