• bzoj3141: [Hnoi2013]旅行


    Description

     

    Input

    第 一行为两个空格隔开的正整数n, m,表示旅行的城市数与旅行所花的月数。接下来n行,其中第 i行包含两个空格隔开的整数Ai和Bi,Ai表示他第i个去的城市编号。Bi为0或1;如果 Bi=0则表示城市Ai没有小L想去的景点,如果Bi=1则表示城市Ai有小L想去的景点,
    Ai两两不同且有1<=Ai<=N,即{Ai}为1,2....N的一个排列。
    例如{2,1,3,4...N}
    N<=500000,M<=200000

    Output

    t仅包括一行,包含m个空格隔开的正整数X1,X2...Xm,t仅包括一行,包含m个空格隔开的正整数X1,X2...Xm,为给小L安排的旅行计划对应的路线。为给小L安排的旅行计划对应的路线。

    Sample Input

    8 3
    2 0
    3 1
    4 1
    1 0
    5 0
    6 1
    7 1
    8 0

    Sample Output

    1 6 8

    HINT

    1个月得到2点快乐值与2点疲劳值,第2个月得到1点快乐值与1点疲劳值,第3 个月得到1点快乐值与1点疲劳值。3个月中疲劳值与快乐值差的最大值为0,达到所有方案最小值。

    可行方案有:

    1  6 8

    3  6 8

    3  1 8


    其中1 6 8字典序最小。

    题解:

    数组b[i]表示城市a[i]是否为有小L想去的景点,1表示有,-1表示没有

    记sum[i]=b[i+1]+b[i+2]+...+b[n],S=sum[0],最大值最小为d,则

    if S==0

      if sum[i]==0 的个数 >= m 则将这些等于0的数做一遍单调队列

      else d=1

    else d=ceil(abs(S)/m)

    证明见http://cxjyxx.me/?p=329

    然后就是如何求字典序最小

    我们每次要找到一个 能使后面的数仍然有解的 最小的一个数作为结尾

    设这个新区间是第i个区间,结束点为k

    首先这个新区间要合法,则abs(S-sum[k])/(m-i)<=d

    然后要保证后面要有解,则ceil(abs(sum[k])/(m-i))<=d

    然后用单调队列维护每种sum值的最小值

    具体见代码

    code:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<cstring>
     5 #include<algorithm>
     6 #define maxn 500005
     7 #define base maxn
     8 using namespace std;
     9 char ch;
    10 bool ok;
    11 void read(int &x){
    12     for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1;
    13     for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    14     if (ok) x=-x;
    15 }
    16 struct Point{int v,id;};
    17 int cnt;
    18 struct node{
    19     Point p;
    20     int pre,next;
    21 }T[maxn<<1];
    22 int newnode(Point p,int pre,int next){T[++cnt]=(node){p,pre,next};return cnt;}
    23 struct queue{
    24     int siz,head,tail;
    25     bool empty(){return !siz;}
    26     void push_front(Point p){
    27         if (!siz) head=tail=newnode(p,0,0);
    28         else T[head].pre=newnode(p,0,head),head=T[head].pre;
    29         ++siz;
    30     }
    31     void push_back(Point p){
    32         if (!siz) head=tail=newnode(p,0,0);
    33         else T[tail].next=newnode(p,tail,0),tail=T[tail].next;
    34         ++siz;
    35     }
    36     void pop_front(){siz--,head=T[head].next;}
    37     void pop_back(){siz--,tail=T[tail].pre;}
    38     Point front(){return T[head].p;}
    39     Point back(){return T[tail].p;}
    40 }list[maxn<<1];
    41 int tot,now[maxn<<1],pre[maxn<<1];
    42 Point point[maxn<<1];
    43 int n,m,last,val[maxn],sum[maxn],rest[maxn];
    44 void add(int u,Point p){pre[++tot]=now[u],now[u]=tot,point[tot]=p;}
    45 int calc(){
    46     int ans=0;
    47     for (int i=n;i>=1;i--){
    48         if (!sum[i]) ans++,rest[i]=1;
    49         rest[i]+=rest[i+1];
    50     }
    51     if (ans>=m) return 0; else return 1;
    52 }
    53 void push(int u,Point p){
    54     while (!list[u].empty()&&p.v<list[u].back().v) list[u].pop_back();
    55     list[u].push_back(p);
    56 }
    57 Point calc(int u,int lim){
    58     for (int p=now[u];p&&point[p].id<=lim;p=pre[p]) push(u,point[p]),now[u]=p;
    59     while (!list[u].empty()&&list[u].front().id<=last) list[u].pop_front();
    60     return list[u].empty()?(Point){maxn,maxn}:list[u].front();
    61 }
    62 Point min(Point a,Point b){return a.v<b.v?a:b;}
    63 int main(){
    64     read(n),read(m);
    65     for (int i=1;i<=n;i++) read(val[i]),read(sum[i-1]),sum[i-1]=(sum[i-1])?1:-1;
    66     for (int i=n-1;i>=0;i--) sum[i]+=sum[i+1];
    67     for (int i=n;i>=1;i--) add(sum[i]+base,(Point){val[i],i});
    68     int S=sum[0],d=(S!=0)?(int)ceil(1.0*abs(S)/(1.0*m)):calc();
    69     if (d){
    70         for (int i=1;i<m;i++){
    71             Point ans=(Point){maxn,maxn};
    72             for (int j=S-d+base;j<=S+d+base;j++){
    73                 if (ceil(abs(1.0*j-base)/(1.0*m-i))<=d)
    74                     ans=min(ans,calc(j,n-(m-i)));
    75             }
    76             printf("%d ",ans.v);
    77             last=ans.id,S=sum[last];
    78         }
    79         printf("%d
    ",val[n]);
    80     }
    81     else{
    82         for (int i=1,p=now[base];i<m;i++){
    83             for (;p&&rest[point[p].id]-1>=m-i;p=pre[p]) push(base,point[p]);
    84             printf("%d ",list[base].front().v);
    85             list[base].pop_front();
    86         }
    87         printf("%d
    ",val[n]);
    88     }
    89     return 0;
    90 }
  • 相关阅读:
    《JAVA高并发编程详解》-Thread start方法的源码
    《JAVA高并发编程详解》-Thread对象的启动
    作为程序员,我建议你学会写作
    【灵异短篇】这个夜晚有点凉
    JAVA中for与while关于内存的细节问题
    通过本质看现象:关于Integer受内部初始化赋值范围限制而出现的有趣现象
    【设计模式】抽象工厂模式
    【设计模式】工厂模式
    【设计模式】单例模式
    【设计模式】基本介绍
  • 原文地址:https://www.cnblogs.com/chenyushuo/p/5090137.html
Copyright © 2020-2023  润新知