zoukankan      html  css  js  c++  java
  • HNOI2013旅行

     一道欺负我智商的题。。。

    本来想打单调队列优化dp的,结果看到算法标签就点了此题

     洛谷题面

    首先你要理解题意,蒟蒻理解了好久。它就是说,给你一个由1和-1组成的数列,让你分成m段,并让这m段区间和最大值最小,还要求多种方案时字典序最小。

    我也不知道大佬怎么做的,反正我不会高斯消元。。。

    哦,对了,如果输入的是0,表示他不喜欢则那一位为-1。

    设总和为S。区间和最小值为ans。后缀和为sum[],后缀中0的个数为cnt[]。

    为什么是后缀,往后看。。。

    首先考虑特殊情况:

    • 全是1 显然答案为ans=ceil(S/m);ceil()是向上取整。
    • 全是-1 ans=ceil(abs(S)/m)
    • 一半全1,一半全-1 比如11111-1-1-1可以变成11(111-1-1-1),括号里为0,可以与任意区间搭配,于是变成了上面的情况。


    所以ans=ceil(abs(S)/m),简易证明:你可以用第三中方法狂消1和-1直到只有一种数,剩下来的数的个数是abs(S)。

    如果abs(S)=0且能分的区间不足m那就另当别论。。。

    由于我太菜了,还有情况没考虑就多多包含

    先预处理sum[],ans,cnt[];

    •  S=0
    • cnt[1]>=m,此时找sum[i]=0的点i,用单调队列维护找出字典序最小的一条即可。
    • cnt[1]<m,ans>0,和下面一起处理。
    • S!=0 sum的每一种取值分开考虑。


      设上一个选的为last,则这一个i能选要满足abs(sum[last+1]-sum[i+1])<=ans,那么我们枚举sum[i+1]的取值时就可以直接从sum[last+1]-ans到sum[last+1]+ans。
      并且abs(sum[i+1]/m'(即剩下要选的数量))要满足小于等于ans,i还有后面的数不能超过m'个。
      然后跑单调队列就完啦,不要告诉我你切黑题还不会这个。。。

    实现起来还有不少细节,比如负数下标之类的,仔细看下应该都能懂

    如果你想TLE的话deque走起

    #include<cstdio>
    #include<algorithm>
    const int N=5e5+5;
    int a[N],sum[N],cnt[N],tot;
    struct node
    {
        int l,r,val;
    }p[N<<1];
    inline int newnode(int l,int r,int val)
    {
        p[++tot]=(node){l,r,val};
        return tot;
    }
    struct que
    {
        int start,end,len;
        inline void push_back(int x)
            {
                if(!len)start=end=newnode(0,0,x);
                else p[end].r=newnode(end,0,x),end=p[end].r;
                ++len;
            }
        inline int empty(){return !len;}
        inline int front(){return p[start].val;}
        inline int back(){return p[end].val;}
        inline void pop_front(){start=p[start].r;--len;}
        inline void pop_back(){end=p[end].l;--len;}
        inline void push(int x)
            {
                while(!empty()&&a[back()]>a[x])pop_back();
                push_back(x);
            }
    }dui[N<<1],dui1[N<<1],*q=dui+N,*q1=dui1+N;
    inline int min(const int &x,const int &y)
    {return a[x]<a[y]?x:y;}
    int main()
    {
        int n,m,ans;scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)scanf("%d%d",&a[i],&sum[i]),sum[i]=sum[i]?1:-1;
        for(int i=n-1;i;--i)sum[i]+=sum[i+1];
        for(int i=n;i;--i){cnt[i]+=cnt[i+1];if(!sum[i])++cnt[i];}
        cnt[n+1]=-1;
    //    for(int i=1;i<=n+1;++i)printf("%d %d
    ",sum[i],cnt[i]);
        int s=sum[1];
        ans=s?(abs(s)-1)/m+1:cnt[1]<m;//printf("ss%d
    ",ans);
        if(ans)
        {
            a[n+1]=n+1;int la=0;
            for(int i=2;i<=n;++i)
                q1[sum[i]].push_back(i-1);
            for(int i=1;i<m;++i)
            {
                int aa=n+1;
                for(int j=sum[la+1]-ans;j<=sum[la+1]+ans;++j)
                {
                    if((abs(j)+m-i-1)/(m-i)>ans)continue;
                    while(!q1[j].empty()&&n-q1[j].front()>=m-i){if(q1[j].front()>la)q[j].push(q1[j].front());q1[j].pop_front();}
                    while(!q[j].empty()&&q[j].front()<=la){q[j].pop_front();}
                    if(!q[j].empty())aa=min(aa,q[j].front());
                }
                la=aa;
                printf("%d ",a[aa]);
            }
        }
        else
        {
            for(int i=1,j=2;i<m;++i)
            {
                for(;cnt[j+1]>=m-i;++j)
                    if(!sum[j+1])
                        q[0].push(j);
                printf("%d ",a[q[0].front()]);
                q[0].pop_front();
            }
        }
        printf("%d
    ",a[n]);
        return 0;
    }
  • 相关阅读:
    需求管理是CMM可重复级中的6个关键过程域之一,其主要目标是__________。A.客观地验证需求管理活动
    47、软件需求工程的活动可以划分为5个独立的阶段:需求获取、需求建模、形成需求规格、需求验证和需求管理,需求建模是()
    JavaScript alert()函数
    系统讲解一下,Dao,Entity,Servlet,Action各自有什么东西-Java/Web开发
    用Eclipse 开发Dynamic Web Project应用程序
    Connection conn = DriverManager.getConnection("jdbc:odbc:bbs");
    linux 常用快捷命令
    Docker 命令大全
    小白入门之十七:yum源配置并使用其安装软件包
    Linux从一般用户切换到root用户
  • 原文地址:https://www.cnblogs.com/cx233666/p/8708482.html
Copyright © 2011-2022 走看看