• 【LOJ #534】「LibreOJ Round #6」花团


    Description

    LOJ #534

    物品集合 (S) 初始为空,按时间递增顺序依次给出 (q) 次操作,操作如下:

    • 1 v w e:表示在 (S) 中加入一个体积为 (v) 价值为 (w) 的物品,第 (e) 次操作结束之后移除该物品。

    • 2 v:表示询问。你需要回答:

      1. 当前 (S) 是否存在一个子集使得子集中物品体积和为 (v)
      2. 当前 (S) 的所有物品体积和为 (v) 的子集中,价值和最大是多少(空集的价值和为 0)。

    本题强制在线。

    数据范围:(1 leq q leq 15000)(1 leq v_i leq 15000)(0 leq w_i leq 15000)

    Solution

    如果没有强制在线的话,可以考虑线段树分治,大家应该都会。

    对时间轴建一棵线段树。

    就是对于每个插入操作,将物品信息挂在线段树上覆盖插入区间的 (mathcal{O}(log q)) 个节点上的 vector 里。

    然后遍历整棵树,若当前到达了某个点,就对该点的 vector 上挂着的所有物品跑一遍 (0/1) 背包。然后递归左右儿子。若当前到达了叶节点,是询问的话就直接回答询问即可。回溯时要撤销物品影响。

    时间复杂度 (mathcal{O}(qv log q))说实话我也不知道这个复杂度是怎么跑过去的。
    值得注意的是,如果直接开栈储存 dp 数组的修改信息时,空间复杂度会达到 (mathcal{O}(qv log q))

    事实上,我们可以多开一维,记录 dp 数组所处理的节点的深度信息。
    例如:f[dep][] 就表示处理到第 ( ext{dep}) 层的某一个节点时,有关该节点的 dp 数组。

    在跑 (0/1) 背包的时候,就直接在 f[dep][] 上进行 dp。
    在往左右儿子递归的时候,用 (mathcal{O}(v)) 的时间将 f[dep][] 的信息继承给 f[dep + 1][]
    可以归纳证明该做法的正确性,这里留给读者。

    这样做,空间复杂度就降到了 (mathcal{O}(v log q))


    如果一定要强制在线的话,还是可以考虑线段树分治。

    注意到本题有一个特殊条件:给出物品时同时给出了移除时间。
    并且在线段树上遍历的过程,时间是从左到右 (1 o q) 的顺序进行访问的。

    这意味这我们可以在没有读入 (q) 个操作时,直接开始遍历过程。

    当到达了叶节点时,将操作读进来。如果是插入操作,则将物品信息挂在线段树上覆盖该插入区间的 (mathcal{O}(log q)) 个节点上的 vector 里,显然这样插入,这些被操作的节点一定不会出现曾经被遍历过的节点,也就不影响到曾经的操作了。如果是询问操作,则直接回答询问即可,记得更新 ( ext{lastans})

    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector> 
    
    using namespace std;
    
    inline int read() {
    	int x = 0, f = 1; char s = getchar();
    	while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
    	while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
    	return x * f;
    }
    
    const int N = 100100;
    const int INF = 1e9;
    
    int Q, m, T;
    
    int lastans;
    
    vector< pair<int, int> > art[N * 4];
    
    void insert(int p, int l, int r, int s, int e, pair<int, int> x) {
    	if (s <= l && r <= e) {
    		art[p].push_back(x);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if (s <= mid)
    		insert(p * 2, l, mid, s, e, x);
    	if (mid < e)
    		insert(p * 2 + 1, mid + 1, r, s, e, x);
    }
    
    int f[20][N];
    
    void solve(int p, int l, int r, int dep) {
    	for (int j = 0; j < (int)art[p].size(); j ++) {
    		pair<int, int> x = art[p][j];
    		int w = x.first, v = x.second;
    		for (int i = m; i >= w; i --)
    			f[dep][i] = max(f[dep][i], f[dep][i - w] + v);
    	}
    
    	if (l == r) {
    		int opt = read();
    		int d = lastans * T;
    
    		switch (opt) {
    			case 1: {
    				int w = read() - d, v = read() - d, e = read() - d;
    				insert(1, 1, Q, l + 1, e, make_pair(w, v));
    				break;
    			}
    
    			case 2: {
    				int v = read() - d;
    				int X, Y;
    
    				if (f[dep][v] > -INF) X = 1, Y = f[dep][v];
    				else X = Y = 0;
    
    				printf("%d %d
    ", X, Y);
    				lastans = X ^ Y;
    
    				break;
    			}
    		}
    	} else {
    		int mid = (l + r) >> 1;
    
    		for (int i = 0; i <= m; i ++) f[dep + 1][i] = f[dep][i];
    		solve(p * 2, l, mid, dep + 1);
    
    		for (int i = 0; i <= m; i ++) f[dep + 1][i] = f[dep][i];
    		solve(p * 2 + 1, mid + 1, r, dep + 1);
    	}
    }
    
    int main() {
    	Q = read(), m = read(), T = read();
    
    	f[0][0] = 0;
    	for (int i = 1; i <= m; i ++)
    		f[0][i] = -INF * 2;
    
    	solve(1, 1, Q, 0);
    
    	return 0;
    }
    
    keep the love forever.
  • 相关阅读:
    复制某文件夹及其子文件夹中的一定大小的文件
    一个简单的查询脚本
    写一个交互的脚本
    nginx+php5.6.12+discuz
    curl 错误
    python 交互界面tab补全
    uwsgi.xml
    supervisorctl
    认识nginx配置文件
    nginx+uwsgi+django 配置3
  • 原文地址:https://www.cnblogs.com/cjtcalc/p/14696077.html
Copyright © 2020-2023  润新知