51nod 1821 最优集合(思维+单调队列)

题意:一个集合S的优美值定义为:最大的x,满足对于任意i∈[1,x],都存在一个S的子集S‘,使得S‘中元素之和为i。

给定n个集合,对于每一次询问,指定一个集合S1和一个集合S2,以及一个数k,要求选择一个S2的子集S3(|S3|<=k),使得S1∪S3的优美值最大。

(集合元素可以重复)

我们首先考虑对于集合S1,能否求出它的最大优美值。

  首先排序一遍,对于前i个元素,如果它的最大优美值为v,那么当S1[i+1]>v+1时,前i+1个元素的最大优美值依然为v,否则为v+S1[i+1].此处易证。

  也就是说集合S1的最大优美值等于排序后集合S1的使得S1[i+1]>Sum[i]+1成立的最大前缀和.

再考虑S2到底加入哪些元素可以使得S1的优美值最大呢。

  首先可以肯定的是,由于加入的元素数量有限制,因此S2应该尽量加对S1的优美值增加最大的元素,由上面可以得知,应该为使得S2[i]<=v+1的最大的S2[i].

  这样之后v就变成了v+S2[i],此时集合S1又有可能有元素可以利用了,这样一直进行K次。

要确保算法复杂度即可能低的情况下,可以使用单调队列来维护上面的操作。

因此,总时间复杂度为O(log(nm)+T*m).

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <bitset>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
inline int Scan() {
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
inline void Out(int a) {
    if(a<0) {putchar(‘-‘); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+‘0‘);
}
const int N=1005;
//Code begin...

VI v[N];
LL val[N], que[N];
int p[N], head, tail;

void sol(int x, int &q, LL &w){
    FO(i,q,v[x].size()) {
        if (v[x][i]>w+1) break;
        w+=v[x][i]; ++q;
    }
}
int main ()
{
    int n, m, x, A, B, K, T;
    n=Scan();
    FOR(i,1,n) {
        m=Scan();
        while (m--) x=Scan(), v[i].pb(x);
        sort(v[i].begin(),v[i].end());
        p[i]=m;
        FO(j,0,v[i].size()) {
            if (v[i][j]>val[i]+1) {p[i]=j; break;}
            val[i]+=v[i][j];
        }
    }
    T=Scan();
    while (T--) {
        A=Scan(); B=Scan(); K=Scan();
        int q=p[A];
        LL w=val[A];
        head=-1; tail=0;
        FO(i,0,v[B].size()) {
            while (v[B][i]>w+1) {
                if (head<tail || !K) break;
                if (head>=tail&&K) w+=que[head], sol(A,q,w), --head, --K;
            }
            if (v[B][i]<=w+1) {
                que[++head]=v[B][i];
                while (head-tail+1>K) ++tail;
            }
            if (head<tail || !K) break;
        }
        if (K) while (head>=tail) w+=que[tail], sol(A,q,w), ++tail;
        printf("%lld\n",w);
    }
    return 0;
}

时间: 2024-08-03 17:21:10

51nod 1821 最优集合(思维+单调队列)的相关文章

1821 最优集合(单调栈+模拟)

1821 最优集合 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 一个集合S的优美值定义为:最大的x,满足对于任意i∈[1,x],都存在一个S的子集S',使得S'中元素之和为i. 给定n个集合,对于每一次询问,指定一个集合S1和一个集合S2,以及一个数k,要求选择一个S2的子集S3(|S3|<=k),使得S1∪S3的优美值最大. (集合元素可以重复) Input 第一行一个数n,(n<=1000) 接下来n行,每行描述一个集合: 第一个数m,表示集合大小,

2018 Multi-University Training Contest 3 1001 / hdu6319 Problem A. Ascending Rating 单调队列,思维

Problem A. Ascending Rating 题意: 给定一个序列a[1..n],对于所有长度为m的连续子区间,求出区间的最大值以及从左往右扫描该区间时a的最大值的变化次数. 1≤m≤n≤107. Shortest judge solution: 534 bytes 题解: 官方题解:按照r从m到n的顺序很难解决这个问题.考虑按照r从n到m的顺序倒着求出每个区间的答案.按照滑窗最大值的经典方法维护a的单调队列,那么队列中的元素个数就是最大值的变化次数.时间复杂度O(n). 最大值好算,

POJ 1821 Fence(单调队列优化DP)

题解 以前做过很多单调队列优化DP的题. 这个题有一点不同是对于有的状态可以转移,有的状态不能转移. 然后一堆边界和注意点.导致写起来就很难受. 然后状态也比较难定义. dp[i][j]代表前i个人涂完前j个位置的最大收益. 然后转移考虑 第i个人可以不刷.dp[i][j]=dp[i-1][j]; 第j个木板可以不刷dp[i][j]=dp[i][j-1]; 然后当c[i].s<=j<=s[i]+l[i]-1时 dp[i][j]=p[i]*j+max(dp[i-1][k]-p[i]*k)其中j-

背包问题入门(单调队列优化多重背包

背包问题 写这篇文章主要是为了帮帮新人吧,dalao勿喷.qwq 一般的背包问题问法 每种物品都有一个价值w和体积c.//这个就是下面的变量名,请看清再往下看. 你现在有一个背包容积为V,你想用一些物品装背包使得物品总价值最大. 01背包 多种物品,每种物品只有一个.求能获得的最大总价值. 我们考虑是否选择第i件物品时,是需要考虑前i-1件物品对答案的贡献的. 分析 如果我们不选择第i件物品,那我们就相当于是用i-1件物品,填充了体积为v的背包所得到的最优解. 而我们选择第i件物品的时候,我们要

单调栈/单调队列/RMQ

在上上周的交友大会中,队长大人提到了st算法,然后仔细的发呆了一个星期,于是就开始做队长的专题了, 6天后的我总算在此专题做题数目和队长一样了..明早没课,准备通宵把这几天的零散的记忆整理一下. HDU 3530 Subsequence 一开始想为何不能m和k一起放到while语句里进行处理 nowmax和nowmin保存了i之前的最大和最小值,假设此时已经出现不满足k和m的序列(A)了(比k大or比m小or both),然后我们往后找,发现了一个比序列(A)的min更小的值(me),此时now

最大子序列和(单调队列算法)

题目大意: 给定一个长度为N的序列,请你求出它最大长度不超过M的最大子序列的和(其中 N,M<=3*10^5) 分析: 一般对于这样的题目,我们最现实想到的就是前缀和,通过枚举序列可以得到答案,但这样的时间复杂度显然是不乐观的(TLE) 所以我们可以通过队列来优化  (这个算法我们称之为单调队列算法) 我们先枚举子序列的有端点 i 此时问题转变为寻找一个 j 最为子序列的左端点 (i-m <= j <= i-1),使得是S[ j ] 最小 (S数组表示前缀和) 这时我们不妨再设一个 k

hdu 6444 网络赛 Neko&#39;s loop(单调队列 + 裴蜀定理)题解

题意:有编号为0~n-1的n个游戏,每个活动都有一个价值(可为负),给你m,s和k,你可以从任意一个编号开始玩,但是下一个游戏必须是编号为(i + k)%n的游戏,你最多能玩m次游戏,问你如果最后你手里要有s的价值,那么你至少一开始要有多少价值. 思路:由裴蜀定理可以知道,如果有n个值首尾相连,间隔为k地走,那么最后会有一个循环节,这样的循环节一共有gcd(n, k)个,每个循环节长度n / gcd(n, k)个.所以我们只要找出所有循环节,并且把每个循环节的最大价值算出来就行了.对于每个循环节

$Poj3017\ Cut\ The\ Sequence$ 单调队列优化$DP$

Poj   AcWing Description 给定一个长度为N的序列 A,要求把该序列分成若干段,在满足“每段中所有数的和”不超过M的前提下,让“每段中所有数的最大值”之和最小. N<=105,M<=1011,0<Ai<=106 Sol 一篇比较清楚的题解 $OvO$ $F[i]$表示把前$i$个数分成若干段,满足每段中所有数之和不超过$M$的前提下,各段的最大值之和的最小值 不难推出转移方程: 但是直接枚举$j$的做法是$O(N^{2})$的,显然过不去,还要优化. DP转移

HDU 4122 Alice&#39;s mooncake shop 单调队列优化dp

Alice's mooncake shop Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4122 Description The Mid-Autumn Festival, also known as the Moon Festival or Zhongqiu Festival is a popular harvest festival celebrated by Ch