zoukankan      html  css  js  c++  java
  • cf1208 E Let Them Slide(差分+RMQ单调队列)

    题意

    如题目的图所示,每行都可以左右移动,但是数字不允许断开,且不许越界(宽度为w)。

    单独求每一列的最大的和为多少。

     

    思路

    对于每一列来说,在每一行上都有一个可以取到的区间,

    所以,对于一列来说,答案就是每行的区间最大值的和。区间最大值可以用RMQ或者单调队列求。

    一开始题目看错了,以为是w*n<=1e6,其实是w<=1e6,n<=1e6,Σleni<=1e6(leni为第i行的长度),直接上w*n*log,果断T了。

    显然,当leni 比w小很多时,中间会有很多列都可以取到整行的所有数字,所以对于这些列,它们求的区间最大值,其实就是整行的最大值。

    对于头尾的一些列,取不到所有数字,那我们就暴力算出它们可以取到的区间,然后查询。

    由于w*n<=1e12,而中间那些列查出来的最大值其实是一样的,所以可以用差分。

    (一些细节)

    有时候查询出来的区间最大值是负数,但是有时候其实可以取到0(移到空格),而有时候只能取负数(移不到空格)。

    len <= i <= w - len + 1 符合条件的第i列可以取到整行

    当整行都为负数时,

    有时候第len列和第(w-len+1)列只能取负数,

    但是,第len+1列和第(w-len+1 - 1)列一定可以取到空格,也就是0

    所以,我们把第len列和第(w-len+1)列,归为暴力算的部分,因为暴力算的时候,都有特判是否可以取0。

    这样,中间的那些就可以直接max(0,rmq(1,len))了

    代码(RMQ)

    (我的姿势比较差,代码又长,跑的还慢,跑了700+ms)

    #include <stdio.h>
    #include <queue>
    #include <string>
    #include <string.h>
    #include <algorithm>
    #include <math.h>
    #include <set>
    using namespace std;
    typedef long long int ll;
    const int maxn = 1e6 + 10;
    const int inf = 70000000;
    const ll mod = 998244353;
    const ll seed = 131;
    int st[maxn][20],mm[maxn],b[maxn];
    void initRMQ(int n)
    {
        mm[0] = -1;
        for(int i = 1;i <= n;i++){
            mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1:mm[i - 1];
            st[i][0] = b[i];
        }
        for(int j = 1;j <= mm[n];j++){
            for(int i = 1;i + (1 << j) - 1 <= n;i++){
                st[i][j] = max(st[i][j - 1],st[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int rmq(int x,int y)
    {
        int k = mm[y - x + 1];
        return max(st[x][k],st[y - (1 << k) + 1][k]);
    }
    ll ans[maxn];
    int main()
    {
        int n,w,len,l,r;
        ll temp;
        while(scanf("%d%d",&n,&w) != EOF){
            memset(ans,0,sizeof(ans));
            while(n--){
                scanf("%d",&len);
                for(int i = 1;i <= len;i++){
                    scanf("%d",&b[i]);
                }
                initRMQ(len);
    
                //i <= w - len + 1
                //i >= len
                //len <= i <= w - len + 1 符合条件的第i列可以取到整行
                int cnt = w,flag = 0;
    
                //中间
                if(len + 1 <= w - len + 1 - 1){
                    temp = max((ll)rmq(1,len),(ll)0);
                    ans[len + 1] += temp;
                    ans[w - len + 1] -= temp;
                    cnt = len;
                    flag = 1;
                }
    
                //
                for(int i = 1;i <= cnt;i++){
                    if(len >= i)
                        r = i;
                    else
                        r = len;
                    if(w - len < i)//w-len+l=i
                        l = i - (w - len);
                    else
                        l = 1;
    
                    temp = rmq(l,r);
                    if(temp < 0){
                        if(len < i || (w - len) >= i)
                            temp = 0;
                    }
                    ans[i] += temp;
                    ans[i + 1] -= temp;
                }
    
                //
                if(!flag)
                    cnt = len;
                else
                    cnt = 0;
                for(int i = w - len + 1 + cnt;i <= w;i++){
                    if(len >= i)
                        r = i;
                    else
                        r = len;
                    if(w - len < i)//w-len+l=i
                        l = i - (w - len);
                    else
                        l = 1;
    
                    temp = rmq(l,r);
                    if(temp < 0){
                        if(len < i || (w - len) >= i)
                            temp = 0;
                    }
                    ans[i] += temp;
                    ans[i + 1] -= temp;
                }
            }
    
            for(int i = 1;i <= w;i++){
                ans[i] += ans[i - 1];
                printf("%lld ",ans[i]);
            }
            puts("");
        }
        return 0;
    }
    View Code

     代码(单调队列)

     很久没写了,正好复习一下。

    单调队列O(n),RMQ O(nlogn),还以为会快一些,但是也一样跑了700+ms

    #include <stdio.h>
    #include <queue>
    #include <string>
    #include <string.h>
    #include <algorithm>
    #include <math.h>
    #include <set>
    using namespace std;
    typedef long long int ll;
    const int maxn = 1e6 + 10;
    const int inf = 70000000;
    const ll mod = 998244353;
    const ll seed = 131;
    int qu[maxn],b[maxn];//qu存下标
    ll ans[maxn];
    int main()
    {
        int n,w,len,l,r;
        ll temp,mx;
        while(scanf("%d%d",&n,&w) != EOF){
            memset(ans,0,sizeof(ans));
            while(n--){
                mx = 0;
                scanf("%d",&len);
                for(int i = 1;i <= len;i++){
                    scanf("%d",&b[i]);
                    mx = max(mx,(ll)b[i]);
                }
    
                //i <= w - len + 1
                //i >= len
                //len <= i <= w - len + 1 符合条件的第i列可以取到整行
                int cnt = len,flag = 0;
    
                //中间
                if(len + 1 <= w - len + 1 - 1){
                    ans[len + 1] += mx;
                    ans[w - len + 1] -= mx;
                    flag = 1;
                }
    
                //
                int head = 0,tail = -1;
                for(int i = 1;i <= cnt;i++){
                    while(head <= tail && b[qu[tail]] <= b[i])
                        tail--;
                    qu[++tail] = i;
                    while(qu[head] < i - (w - len))
                        head++;
    
                    temp = b[qu[head]];
                    if(temp < 0){
                        if(len < i || (w - len) >= i)
                            temp = 0;
                    }
                    ans[i] += temp;
                    ans[i + 1] -= temp;
                }
    
                //
                if(!flag)
                    cnt = len + 1;
                else
                    cnt = w - len + 1;
                head = 0;tail = -1;
                for(int i = w;i >= cnt;i--){
                    int pos = i - (w - len);
                    while(head <= tail && b[qu[tail]] <= b[pos])
                        tail--;
                    qu[++tail] = pos;
                    while(qu[head] > pos + (w - len))
                        head++;
    
                    temp = b[qu[head]];
                    if(temp < 0){
                        if(len < i || (w - len) >= i)
                            temp = 0;
                    }
                    ans[i] += temp;
                    ans[i + 1] -= temp;
                }
            }
    
            for(int i = 1;i <= w;i++){
                ans[i] += ans[i - 1];
                printf("%lld ",ans[i]);
            }
            puts("");
        }
        return 0;
    }
    View Code

     

  • 相关阅读:
    灰度图转换
    OGRE分析之文件系统 (1)
    屏幕截图
    [GP]template必须定义于头文件中
    OGRE分析之设计模式
    ON_COMMAND_RANGE和ON_UPDATE_COMMAND_UI_RANGE
    使用SkinMagic Toolkit美化界面(II)
    Single Sign On for Windows and Linux
    "C compiler cannot create executables"
    How to Create a First C Program on Linux
  • 原文地址:https://www.cnblogs.com/InitRain/p/12337366.html
Copyright © 2011-2022 走看看