Description
给你 nnn 种物品,第 iii 种价格为 viv_ivi,价值为 wiw_iwi,数量为 sis_isi。
定义 f(x)f(x)f(x) 为:在总价格 ≤x\le x≤x 的前提下任选这些物品,所能获得的最大价值。
给你 qqq 个询问 (l,r)(l,r)(l,r),你需要求出删除第 l∼rl\sim rl∼r 种物品后,(∑i=1mf(i)) mod (108+7)(\sum_{i=1}^m f(i))\bmod (10^8+7)(∑i=1mf(i))mod(108+7) 和 ⊕i=imf(i)\oplus_{i=i}^m f(i)⊕i=imf(i) 的值,其中 mmm 为给定常数。
询问间互相独立,且强制在线。
Limitations
本题有多测
1≤n,m,q,vi,si≤1031\le n,m,q,v_i,s_i\le 10^31≤n,m,q,vi,si≤103
1≤wi≤2.5×1051\le w_i\le 2.5\times 10^51≤wi≤2.5×105
∑n,∑m,∑q≤104\sum n,\sum m,\sum q\le 10^4∑n,∑m,∑q≤104
3s,64MB3\text{s},64\text{MB}3s,64MB
Solution
显然是多重背包。
我们可以想到预处理出前后缀的答案,查询时直接合并 [1,l)[1,l)[1,l) 和 (r,n](r,n](r,n] 两个背包,但合并背包的复杂度是 O(m2)O(m^2)O(m2) 的,过不了。
注意到合并的复杂度过高,必须舍弃,然而本题强制在线,用不了猫树分治,考虑万能的分块。
我们预处理 g(bl,br,x)g(\textit{bl},\textit{br},x)g(bl,br,x) 表示排除第 bl∼br\textit{bl}\sim\textit{br}bl∼br 块内的物品后后 f(x)f(x)f(x) 的值,每次询问先取整块,再暴力加入散块即可。
现在只需要维护能够动态插入物品的多重背包,这是容易做到的(需要二进制或单调队列优化),于是做完了,代码很好写。
用二进制优化的复杂度是 O(nmnlogn)O(nm\sqrt n\log n)O(nmnlogn),用单调队列可以去掉 log\loglog 但没必要。两者空间复杂度均为 O(nm)O(nm)O(nm)。
Code
2.34KB,8.03s,3.65MB (C++20 with O2)2.34\text{KB},8.03\text{s},3.65\text{MB}\;\texttt{(C++20 with O2)}2.34KB,8.03s,3.65MB(C++20 with O2)
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
constexpr int mod = 1e8 + 7;
struct info {
int m;
vector<int> dp;
inline info() {}
inline info(int _m) : m(_m), dp(_m + 1) {}
inline void _add(int v, int w) {
for (int j = m; j >= v; j--) chmax(dp[j], dp[j - v] + w);
}
inline void add(int v, int w, int s) {
int k = 1;
while (k <= s) _add(k * v, k * w), s -= k, k <<= 1;
if (s) _add(s * v, s * w);
}
inline int operator[](int i) const { return dp[i]; }
};
inline void solve() {
int n, m, q;
cin >> n >> m >> q;
vector<int> v(n), w(n), s(n);
for (int i = 0; i < n; i++) cin >> v[i];
for (int i = 0; i < n; i++) cin >> w[i];
for (int i = 0; i < n; i++) cin >> s[i];
const int B = 0.8 * sqrt(n) + 1, blocks = (n + B - 1) / B;
vector<int> L(blocks), R(blocks);
for (int i = 0; i < blocks; i++) L[i] = i * B, R[i] = min(L[i] + B, n) - 1;
info now(m);
vector dp(blocks, vector<info>(blocks));
for (int bl = 0; bl < blocks; bl++) {
info cur = now;
for (int br = blocks - 1; br >= bl; br--) {
dp[bl][br] = cur;
for (int i = L[br]; i <= R[br]; i++) cur.add(v[i], w[i], s[i]);
}
for (int i = L[bl]; i <= R[bl]; i++) now.add(v[i], w[i], s[i]);
}
auto ask = [&](int l, int r) {
int bl = l / B, br = r / B;
info ans = dp[bl][br];
for (int i = L[bl]; i < l; i++) ans.add(v[i], w[i], s[i]);
for (int i = R[br]; i > r; i--) ans.add(v[i], w[i], s[i]);
return ans;
};
for (int i = 0, l, r, lastans = 0; i < q; i++) {
cin >> l >> r;
l = (l + lastans - 1) % n;
r = (r + lastans - 1) % n;
if (l > r) swap(l, r);
auto ans = ask(l, r);
int sum = 0, xor_sum = 0;
for (int j = 1; j <= m; j++) {
sum = (1LL * sum + ans[j]) % mod;
xor_sum ^= ans[j];
}
cout << sum << ' ' << xor_sum << '\n';
lastans = sum;
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T; cin >> T;
while (T--) solve();
return 0;
}

被折叠的 条评论
为什么被折叠?



