LG P7432 [THUPC 2017] 钦妹的玩具商店 Solution

Description

给你 nnn 种物品,第 iii 种价格为 viv_ivi,价值为 wiw_iwi,数量为 sis_isi
定义 f(x)f(x)f(x) 为:在总价格 ≤x\le xx 的前提下任选这些物品,所能获得的最大价值。
给你 qqq 个询问 (l,r)(l,r)(l,r),你需要求出删除第 l∼rl\sim rlr 种物品后,(∑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^31n,m,q,vi,si103
1≤wi≤2.5×1051\le w_i\le 2.5\times 10^51wi2.5×105
∑n,∑m,∑q≤104\sum n,\sum m,\sum q\le 10^4n,m,q104
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}blbr 块内的物品后后 f(x)f(x)f(x) 的值,每次询问先取整块,再暴力加入散块即可。

现在只需要维护能够动态插入物品的多重背包,这是容易做到的(需要二进制或单调队列优化),于是做完了,代码很好写。

用二进制优化的复杂度是 O(nmnlog⁡n)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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值