洛谷P2827 [NOIP2016 提高组] 蚯蚓 (二叉堆/队列)


容易想到的是用二叉堆来解决,切断一条蚯蚓,其他的都要加上一个值,不妨用一个表示偏移量的delta。

1.取出最大的x,x+=delta;

2.算出切断后的两个新长度,都减去delta和q;

3.delta+=q;

将其他长度都加上q不好实现,我们就把新的两条减去p,相对大小关系不变,最后还原即可。

但这还不是正解:

 1 //复杂度mlogn,m过大,要超时 
 2 #include
 3 using namespace std;
 4 int n,m,q,u,v,t;
 5 double p;
 6 priority_queue<int,vector<int>,less<int> > que;
 7 int delta;//记录偏移量 
 8 
 9 int read(){
10     int x=0,f=1;char c=getchar();
11     while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
12     while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
13     return x*f;
14 }
15 
16 int main(){
17     n=read(),m=read(),q=read(),u=read(),v=read(),t=read();
18     p=(double)u/v;
19     for(int i=1;i<=n;i++){
20         int x;x=read();
21         que.push(x);
22     }  
23     for(int i=1;i<=m;i++){
24         int temp;
25         temp=que.top()+delta;que.pop();
26         int a1=temp*p,a2=temp-a1;
27         a1-=delta+q,a2-=delta+q;
28         delta+=q;
29         que.push(a1),que.push(a2);
30         if(i%t==0) cout<" ";
31     }
32     cout<<endl;
33     int num=0;
34     while(que.size()){
35         num++;
36         if(num%t==0)
37             cout<" ";
38         que.pop();
39     } 
40 }

进一步分析发现从集合中取出的数是单调递减的,新的两段长度也是单调递减的,这是容易证明的。

所以有三个队列维护:原长度,新产生的长度1,新产生的长度2:

 1 //隐晦的单调性 
 2 //用队列实现,去掉了优先队列的log,所以复杂度是m+nlogn 
 3 #include
 4 #define N 7000005
 5 using namespace std;
 6 bool cmp(const int &a,const int &b){
 7     return a>b;
 8 }
 9 
10 priority_queue<int>ans;
11 int cut1[N],now[N],cut2[N];
12 int n,m,q,u,v,t;
13 int sigma;
14 double p;
15 int h0,h1,h2;
16 int t0,t1,t2;
17 
18 int main(){
19     scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
20     p=(double)u/v;int tmp;
21     for(int i=1;i<=n;i++){
22         int x;scanf("%d",&x);
23         now[++t0]=x;
24     }
25     h0=h1=h2=1;
26     sort(now+1,now+t0+1,cmp);//对所有蚯蚓从大到小排序
27     int top;
28     for(int i=1;i<=m;++i){
29         if(h0>t0){if(cut1[h1]>cut2[h2])top=cut1[h1++];else top=cut2[h2++];}
30         else if(now[h0]>=cut1[h1]&&now[h0]>=cut2[h2])top=now[h0],++h0;
31         else if(cut1[h1]>=cut2[h2]&&now[h0]<=cut1[h1])top=cut1[h1],++h1;
32         else top=cut2[h2],++h2;
33         //反正是要找三个队列中的最大值,有很多可以实现的做法 
34         top+=sigma;
35         int a1=floor(p*(double)top),a2=top-a1;
36         sigma+=q;
37         a1-=sigma,a2-=sigma;
38         cut1[++t1]=a1,cut2[++t2]=a2;
39         if(i%t==0)printf("%d ",top);
40     }
41     putchar('\n');
42     for(int i=h0;i<=t0;++i)ans.push(now[i]);
43     for(int i=h1;i<=t1;++i)ans.push(cut1[i]);
44     for(int i=h2;i<=t2;++i)ans.push(cut2[i]);
45     for(int i=1;ans.size();++i){
46         if(i%t==0)printf("%d ",ans.top()+sigma);
47         ans.pop();
48     }
49     return 0;
50 }

其他细节的和上一种差不多。