zoukankan      html  css  js  c++  java
  • noip2020模拟赛 背包 (knapsack)

    题目

    区间 (01) 背包
    (1 le l_i le r_i le n le 20000,1 le q le 100000,1 le m_i le 500, 1 le w_i le 500, 1 le v_i le 10^6)

    分析

    显然,我们考虑区间背包的合并
    于是可以考虑分治策略
    我们每次处理跨区间的询问
    那么可以以 (mid) 为起点,往左做一遍后缀背包(不一定装满),往右做一遍前缀背包(一定装满)
    一个在本区间且跨 (mid) 的询问就可以用这些拼起来,统计答案即可
    不在本区间的分治处理即可
    上面限制了背包装不装满是为了不重复的统计
    于是时间复杂度就是 (O(nmlog n + qm))

    (Code)

    #include<cstdio>
    #include<vector>
    #define ls (k << 1)
    #define rs (ls | 1)
    using namespace std;
    
    const int N = 2e4 + 5, M = 505, Q = 1e5 + 5;
    const int INF = 0x3f3f3f3f, P = 998244353;
    int n, q, Mx, v[N], w[N], f[N][M], g[N][M];
    struct node{int l, r, m;}a[Q];
    struct answer{int val, g;}ans[Q];
    vector<int> st[Q << 2];
    
    void solve(int k, int L, int R)
    {
    	if (!st[k].size()) return;
    	int mid = (L + R) >> 1;
    	
    	f[mid][0] = 0, g[mid][0] = 1;
    	for(register int i = mid; i <= R; i++) 
    	{
    		g[i][0] = 1;
    		for(register int j = 1; j <= Mx; j++) f[i][j] = -INF, g[i][j] = 0;
    	}
    	for(register int i = mid + 1; i <= R; i++)
    	{
    		for(register int j = 0; j <= Mx; j++) f[i][j] = f[i - 1][j], g[i][j] = g[i - 1][j]; 
    		for(register int j = w[i]; j <= Mx; j++)
    		{
    			int val = f[i - 1][j - w[i]] + v[i];
    			if (val > f[i][j]) f[i][j] = val, g[i][j] = g[i - 1][j - w[i]];
    			else if (val == f[i][j]) g[i][j] = (g[i][j] + g[i - 1][j - w[i]]) % P;
    		}
    	}
    	
    	for(register int i = mid; i >= L; i--)
    		for(register int j = 0; j <= Mx; j++) f[i][j] = 0, g[i][j] = 1;
    	for(register int i = w[mid]; i <= Mx; i++) f[mid][i] = v[mid], g[mid][i] = 1;
    	for(register int i = mid - 1; i >= L; i--)
    	{
    		for(register int j = 0; j <= Mx; j++) f[i][j] = f[i + 1][j], g[i][j] = g[i + 1][j];
    		for(register int j = w[i]; j <= Mx; j++)
    		{
    			int val = f[i + 1][j - w[i]] + v[i];
    			if (val > f[i][j]) f[i][j] = val, g[i][j] = g[i + 1][j - w[i]];
    			else if (val == f[i][j]) g[i][j] = (g[i][j] + g[i + 1][j - w[i]]) % P;
    		}
    	}
    	
    	for(register int i = 0; i < st[k].size(); i++)
    	{
    		int now = st[k][i], l = a[now].l, r = a[now].r, m = a[now].m;
    		if (l == mid && r == mid) ans[now].val = ((m >= w[mid]) ? (v[mid]) : 0), ans[now].g = 1;
    		else if (l > mid) st[rs].push_back(now);
    		else if (r <= mid) st[ls].push_back(now);
    		else{
    			for(register int j = 0; j <= m; j++)
    			{
    				int val = f[r][j] + f[l][m - j];
    				if (val > ans[now].val) ans[now].val = val, ans[now].g = (long long)g[r][j] * g[l][m - j] % P;
    				else if (val == ans[now].val) ans[now].g = (ans[now].g + (long long)g[r][j] * g[l][m - j]) % P;
    			}
    		}
    	}
    	
    	if (L == R) return;
    	solve(ls, L, mid), solve(rs, mid + 1, R);
    }
    
    int main() 
    {
    	freopen("knapsack.in", "r", stdin);
    	freopen("knapsack.out", "w", stdout);
    	scanf("%d", &n);
    	for(register int i = 1; i <= n; i++) scanf("%d%d", &v[i], &w[i]);
    	scanf("%d", &q);
    	for(register int i = 1; i <= q; i++) 
    		scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].m), Mx = max(Mx, a[i].m), st[1].push_back(i);
    	solve(1, 1, n);
    	for(register int i = 1; i <= q; i++)
    	{
    		if (ans[i].val == 0) printf("0 0
    ");
    		else printf("%d %d
    ", ans[i].val, ans[i].g);
    	}
    }
    
  • 相关阅读:
    086 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 03 面向对象基础总结 01 面向对象基础(类和对象)总结
    085 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 04 构造方法调用
    jQuery UI组件库Kendo UI使用技巧小分享
    Kendo UI ListView模板功能,让Web开发更轻松
    UI组件套包DevExpress ASP.NET Core v20.2新版亮点:全新的查询生成器
    Devexpress WinForms最新版开发.NET环境配置Visual Studo和SQL Server对应版本
    全新的桌面应用数据可视化呈现方式,Sankey Diagram控件你了解多少?
    java中的递归方法
    连接数据库查询 将查询结果写入exce文件中
    java连接mysql数据查询数据
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/14085901.html
Copyright © 2011-2022 走看看