CodeForces 981E Bookshelves(dp+思维)

  • E. Addition on Segments
    time limit per test:2 seconds
    memory limit per test:256 megabytes
    input:standard input
    output:standard output

    Grisha come to a contest and faced the following problem.

    You are given an array of size n

    , initially consisting of zeros. The elements of the array are enumerated from 1 to n. You perform q operations on the array. The i-th operation is described with three integers li, ri and xi (1lirin, 1xin) and means that you should add xi to each of the elements with indices li,li+1,,ri

    . After all operations you should find the maximum in the array.

    Grisha is clever, so he solved the problem quickly.

    However something went wrong inside his head and now he thinks of the following question: "consider we applied some subset of the operations to the array. What are the possible values of the maximum in the array?"

    Help Grisha, find all integers y

    between 1 and n such that if you apply some subset (possibly empty) of the operations, then the maximum in the array becomes equal to y

    .

    Input

    The first line contains two integers nand q (1n,q104) — the length of the array and the number of queries in the initial problem.The following qlines contain queries, one per line. The i-th of these lines contains three integers li, ri and xi (1lirin, 1xin), denoting a query of adding xi to the segment from li-th to ri-th elements of the array, inclusive.

    Output

    In the first line print the only integer k, denoting the number of integers from 1 to n, inclusive, that can be equal to the maximum in the array after applying some subset (possibly empty) of the given operations.

    In the next line print these kntegers from 1 to n— the possible values of the maximum. Print these integers in increasing order.

    Examples
    Input
    4 3
    1 3 1
    2 4 2
    3 4 4
    
    Output
    4
    1 2 3 4 
    
    Input
    7 2
    1 5 1
    3 7 2
    
    Output
    3
    1 2 3 
    
    Input
    10 3
    1 1 2
    1 1 3
    1 1 6
    
    Output
    6
    2 3 5 6 8 9 
    
    Note

    Consider the first example. If you consider the subset only of the first query, the maximum is equal to 1. If you take only the second query, the maximum equals to 2. If you take the first two queries, the maximum becomes 3. If you take only the fourth query, the maximum becomes 4. If you take the fourth query and something more, the maximum becomes greater that n, so you shouldn't print it.    

  •     In the second example you can take the first query to obtain 1.You can take only the second query to obtain 2. You can take all queries to obtain 3In the third example you can obtain the following maximums:

  • You can achieve the maximim of 2 by using queries: (1)

  • You can achieve the maximim of 3by using queries: (2)
  • .You can achieve the maximim of 5by using queries: (1,2)
  • .You can achieve the maximim of 6by using queries: (3)
  • .You can achieve the maximim of 8by using queries: (1,3)
  • .You can achieve the maximim of 9by using queries: (2,3).


        大致题意:给你q个操作和一个长度为n的序列,初始状态序列全为0。每个操作是对区间[l,r]增加v。然后,现在你可以只选择这q个操作中的任意几个进行操作,问你操作完毕之后整个序列中最大值是否可以是1~n,输出可以的数字。也就是说,对于每一个数字i,看能否找到一个操作的子集,使得操作完毕后区间最大值为i。

        可以说刚读完题的时候没有任何想法,完全不知道怎么下手,但是数据范围给了我们提示,n、q分别是1e4级别,那么可以考虑O(nq)的算法。首先,对于询问,我们按照操作左端点l进行排序。其次,再考虑dp[i]表示考虑了j个询问,序列中数值可以等于i的最右边的位置,那么有转移方程dp[i]=max(dp[i],min(dp[i-Q[j].v],Q[j].r)),其中要求Q[j].l>=dp[i-Q[j].v]。含义是说,对于每一个值,可以通过一个操作从之前的值转移过来,而条件这个操作包含之前最右边的点。

        最后,我们可以发现,如果一个数值i可以找到一个子集,那么他对应的dp[i]一定大于0。最后统计个数并输出即可。总的来说转移方程与背包类似,巧妙之处在于想到了这个最右边位置的状态表示,并且恰好对应了能否找到子集。具体见代码:

#include<bits/stdc++.h>
#define LL long long
#define N 10010

using namespace std;

int dp[N];
struct node{int l,r,v;} Q[N];
bool cmp(node a,node b){return a.l<b.l;}

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(0);
    int n,q;
    cin>>n>>q;
    for(int i=1;i<=q;i++)
        cin>>Q[i].l>>Q[i].r>>Q[i].v;
    sort(Q+1,Q+1+q,cmp); dp[0]=N;
    for(int i=1;i<=q;i++)
        for(int j=n;j>=Q[i].v;j--)
        {
            if (dp[j-Q[i].v]<Q[i].l) continue;
            dp[j]=max(dp[j],min(dp[j-Q[i].v],Q[i].r));
        }
    int ans=0;
    for(int i=1;i<=n;i++) if (dp[i]) ans++;
    cout<<ans<<endl;
    for(int i=1;i<=n;i++) if (dp[i]) cout<<i<<" ";
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值