3141: [Hnoi2013]旅行 - BZOJ

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字典序最小。

 

感谢两位的题解http://www.cnblogs.com/lazycal/archive/2013/07/29/3221342.htmlhttp://cxjyxx.me/?p=329

基本上就是他们说的了,本人愚笨,两天才刷掉它

我就随便讲一下
对每一个s[i]做一个单调队列
在单调队列里维护a[i]单调递增
当ans为0时
每次把可以作为解的点加入单调队列,把a[i]比他小的都删掉,然后取队头
当ans不为0时
如果n-m+1可以做现在的解,把n-m+1这个点加进单调队列,操作一样
为什么可以这样做,我认为是因为入队的顺序本来是按从近到远的顺序,而且都可以作为现在的解,如果a[i]比a[j]大,j在i后面,那肯定就把i删除了

  1 var
  2     s,sum,sum2,c:array[0..500010]of longint;
  3     first,tail:array[-500010..500010]of longint;
  4     next,pre,num:array[0..1000010]of longint;
  5     n,m,ans,tot,l:longint;
  6  
  7 function get(l,m:longint):longint;
  8 begin
  9     if sum[l]=0 then
 10       if sum2[l]>=m then exit(0)
 11       else exit(1)
 12     else
 13       begin
 14         get:=abs(sum[l])div m;
 15         if abs(sum[l])mod m>0 then inc(get);
 16       end;
 17 end;
 18  
 19 procedure insert(x:longint);
 20 var
 21     i:longint;
 22 begin
 23     if get(x+1,m-1)>ans then exit;
 24     inc(tot);
 25     num[tot]:=x;
 26     if first[sum[x+1]]=0 then
 27     begin
 28       first[sum[x+1]]:=tot;
 29       tail[sum[x+1]]:=tot;
 30       exit;
 31     end;
 32     i:=tail[sum[x+1]];
 33     while (i<>0)and((c[num[i]]>c[x])or(num[i]<l)) do
 34       begin
 35         if pre[i]=0 then
 36           begin
 37             i:=0;
 38             break;
 39           end;
 40         i:=pre[i];
 41       end;
 42     if i=0 then
 43     begin
 44       first[sum[x+1]]:=tot;
 45       tail[sum[x+1]]:=tot;
 46       exit;
 47     end;
 48     pre[tot]:=i;
 49     next[i]:=tot;
 50     tail[sum[x+1]]:=tot;
 51 end;
 52  
 53 procedure work2;
 54 var
 55     i,k:longint;
 56 begin
 57     l:=1;
 58     k:=1;
 59     while m>1 do
 60       begin
 61         while sum2[k]>=m-1 do
 62           begin
 63             if sum[k+1]=0 then insert(k);
 64             inc(k);
 65           end;
 66         while num[first[0]]<l do
 67           first[0]:=next[first[0]];
 68         pre[first[0]]:=0;
 69         write(c[num[first[0]]],' ');
 70         l:=num[first[0]]+1;
 71         dec(m);
 72       end;
 73     write(c[n]);
 74     halt;
 75 end;
 76  
 77 procedure init;
 78 var
 79     i:longint;
 80 begin
 81     read(n,m);
 82     for i:=1 to n do
 83       begin
 84         read(c[i],s[i]);
 85         if s[i]=0 then s[i]:=-1;
 86       end;
 87     for i:=n downto 1 do
 88       begin
 89         sum[i]:=sum[i+1]+s[i];
 90         sum2[i]:=sum2[i+1];
 91         if sum[i]=0 then inc(sum2[i]);
 92       end;
 93     ans:=get(1,m);
 94     if ans=0 then work2;
 95     for i:=1 to n-m do
 96       insert(i);
 97 end;
 98  
 99 procedure work;
100 var
101     i,j,min,sl,sr:longint;
102 begin
103     l:=1;
104     while m>1 do
105       begin
106         insert(n-m+1);
107         sl:=sum[l]-ans;
108         sr:=ans+sum[l];
109         j:=5000000;
110         min:=5000000;
111         for i:=sl to sr do
112           begin
113             while (first[i]<>0)and((num[first[i]]<l)or(get(num[first[i]]+1,m-1)>ans)) do
114               first[i]:=next[first[i]];
115             pre[first[i]]:=0;
116             if first[i]<>0 then
117             if c[num[first[i]]]<min then
118             begin
119               min:=c[num[first[i]]];
120               j:=num[first[i]];
121             end;
122           end;
123         write(min,' ');
124         l:=j+1;
125         dec(m);
126       end;
127     write(c[n]);
128 end;
129  
130 begin
131     init;
132     work;
133 end.
View Code

 

转载于:https://www.cnblogs.com/Randolph87/p/3585696.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值