码迷,mamicode.com
首页 > 其他好文 > 详细

洛谷2629

时间:2017-10-21 19:04:18      阅读:194      评论:0      收藏:0      [点我收藏+]

标签:div   区间   lin   stdio.h   个数   return   blog   成功   答案   

来一发楼下兄弟没想到的单调队列做法。

这道题其实是预处理难想,单调队列还是很简单的

我是通过找规律做出来的

首先这是样例。

-3 5 1 2

然后这是样例和样例的前缀和

-3 5 1 2

-3 2 3 5

然后这是样例和所有倒叙的可能前缀和(显然这不就是破环为列嘛)

-3 5 1 2

-3 2 3 5

5 6 8 5

1 3 0 5

2-1 4 5

然后我们发现,这个方阵除第一行,最后一列以外

每个数都等于右上角的数减去上一行第一个数。

现在是见证奇迹的时刻!如果我们认为最后一列也满足这个规律,而不是简单的等于上一个数呢?

那显然需要构造一些数了,构造后的方阵如下

-3 5 1 2

-3 2 3 5 2 7 8

5 6 8 5 1011

1 3 0 5 6

2 -14 5

然后我们发现,如果在第一行取一个长度为n的区间

那么d[t]-d[t+i](t为区间的起点)就是第t+1行第i-1个数的值!

(因为第n个永远是正的,不管它)

也就是说,每一个长度为n且起点为最小值的区间,就对应一个方案。

最小值好办,单调队列O(n)扫一遍搞定。

那么如何生成第一行?

根据我们构造的原理(或者找规律)有:

d[i]=d[i-n]+d[n]

然后就可以做啦~

上代码~

#include<stdio.h>
int n;
int sum[2000020];
struct data 
{
    int n;int v;
}q[2000020];//数组是两倍!两倍!
int head=1;int tail=0;//手写队列部分~
inline void push(data x)
{
    //printf("push(%d,%d)\n",x.v,x.n);
    q[++tail]=x;return;
}
inline void pop()
{
    if(head<=tail)head++;
    return;
}
inline void pot()
{
    if(head<=tail)tail--;
    return;
}
inline bool empty()
{
    if(head>tail)
    {
        head=1;tail=0;
        return true;    
    }
    else return false;
}
int res;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&sum[i]);
        sum[i]+=sum[i-1];//处理前缀和
    }
    for(int i=n+1;i<2*n;i++)
    {
        sum[i]=sum[i-n]+sum[n];//处理人造前缀和
    }
    for(int i=1;i<2*n;i++)//单调队列模板
    {   
        if(!empty())
        {
            while(1)
            {
                //printf("tail.v=%d\n",q[tail].v);
                if(q[tail].v>sum[i])
                {
                    pot();if(empty())break;
                }
                else if(q[tail].v<=sum[i])
                {
                    data p;p.v=sum[i]; p.n=i;
                    push(p);break;
                }
            }
        }
        if(empty())
        {
            data p;p.v=sum[i];p.n=i;
            push(p);
        }
        /*printf("sum[%d]=%d\n",i,sum[i]);
        for(int j=head;j<=tail;j++)
        {
            printf("%d ",q[j].v);
        }printf("\n");
        for(int j=head;j<=tail;j++)
        {
            printf("%d ",q[j].n);
        }printf("\n");*/
        if(q[tail].n-q[head].n==n-1)//如果成功构造出长度为n的区间,答案+1
        {
            res++;pop();
        }
    }
    printf("%d",res);
    return 0;//拜拜程序~
}

 

洛谷2629

标签:div   区间   lin   stdio.h   个数   return   blog   成功   答案   

原文地址:http://www.cnblogs.com/SHADOWICENCXIX/p/7705173.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!