1. 程式人生 > >Luogu P2827 蚯蚓(模擬)

Luogu P2827 蚯蚓(模擬)

P2827 蚯蚓

題意

題目描述

本題中,我們將用符號\(\lfloor c\rfloor\)表示對\(c\)向下取整,例如:\(\lfloor 3.0\rfloor =\lfloor 3.1\rfloor =\lfloor 3.9\rfloor =3\)

蛐蛐國最近蚯蚓成災了!隔壁跳蚤國的跳蚤也拿蚯蚓們沒辦法,蛐蛐國王只好去請神刀手來幫他們消滅蚯蚓。

蛐蛐國裡現在共有\(n\)只蚯蚓(\(n\)為正整數)。每隻蚯蚓擁有長度,我們設第\(i\)只蚯蚓的長度為\(a_i(i=1,2,\dots ,n)\),並保證所有的長度都是非負整數(即:可能存在長度為\(0\)的蚯蚓)。

每一秒,神刀手會在所有的蚯蚓中,準確地找到最長的那一隻(如有多個則任選一個)將其切成兩半。神刀手切開蚯蚓的位置由常數\(p\)

(是滿足\(0<p<1\)的有理數)決定,設這隻蚯蚓長度為\(x\),神刀手會將其切成兩隻長度分別為\(\lfloor px\rfloor\)\(x-\lfloor px\rfloor\)的蚯蚓。特殊地,如果這兩個數的其中一個等於\(0\),則這個長度為\(0\)的蚯蚓也會被保留。此外,除了剛剛產生的兩隻新蚯蚓,其餘蚯蚓的長度都會增加\(q\)(是一個非負整常數)。

蛐蛐國王知道這樣不是長久之計,因為蚯蚓不僅會越來越多,還會越來越長。蛐蛐國王決定求助於一位有著洪荒之力的神祕人物,但是救兵還需要\(m\)秒才能到來……(\(m\)為非負整數)

蛐蛐國王希望知道這\(m\)秒內的戰況。具體來說,他希望知道:

  • \(m\)秒內,每一秒被切斷的蚯蚓被切斷前的長度(有\(m\)個數);
  • \(m\)秒後,所有蚯蚓的長度(有\(n+m\)個數)。

蛐蛐國王當然知道怎麼做啦!但是他想考考你……

輸入輸出格式

輸入格式:

第一行包含六個整數\(n,m,q,u,v,t\),其中:\(n,m,q\)的意義見【問題描述】;\(u,v,t\)均為正整數;你需要自己計算\(p=u/v\)(保證\(0<u<v\));\(t\)是輸出引數,其含義將會在【輸出格式】中解釋。

第二行包含\(n\)個非負整數,為\(a_1,a_2,\dots ,a_n\),即初始時\(n\)只蚯蚓的長度。

同一行中相鄰的兩個數之間,恰好用一個空格隔開。

保證\(1\)\leq n\(\leq 10^5\)\(0\leq m\leq 7\times 10^6\)\(0<u<v\leq 10^9\)\(0\leq q\leq 200\)\(1\leq t\leq 71\)\(0\leq a_i\leq 10^8\)

輸出格式:

第一行輸出\(\left \lfloor \frac{m}{t} \right \rfloor\)個整數,按時間順序,依次輸出第\(t\)秒,第\(2t\)秒,第\(3t\)秒,……被切斷蚯蚓(在被切斷前)的長度。

第二行輸出\(\left \lfloor \frac{n+m}{t} \right \rfloor\)個整數,輸出\(m\)秒後蚯蚓的長度;需要按從大到小的順序,依次輸出排名第\(t\),第\(2t\),第\(3t\),……的長度。

同一行中相鄰的兩個數之間,恰好用一個空格隔開。即使某一行沒有任何數需要輸出,你也應輸出一個空行。

請閱讀樣例來更好地理解這個格式。

輸入輸出樣例

輸入樣例#1:

3 7 1 1 3 1
3 3 2

輸出樣例#1:

3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2

輸入樣例#2:

3 7 1 1 3 2
3 3 2

輸出樣例#2:

4 4 5
6 5 4 3 2

輸入樣例#3:

3 7 1 1 3 9
3 3 2

輸出樣例#3:

//空行
2

說明

【樣例解釋1】

在神刀手到來前:\(3\)只蚯蚓的長度為\(3,3,2\)

\(1\)秒後:一隻長度為\(3\)的蚯蚓被切成了兩隻長度分別為\(1\)\(2\)的蚯蚓,其餘蚯蚓的長度增加了\(1\)。最終\(4\)只蚯蚓的長度分別為\((1,2),4,3\)。括號表示這個位置剛剛有一隻蚯蚓被切斷。

\(2\)秒後:一隻長度為44的蚯蚓被切成了\(1\)\(3\)\(5\)只蚯蚓的長度分別為:\(2,3,(1,3),4\)

\(3\)秒後:一隻長度為\(4\)的蚯蚓被切斷。\(6\)只蚯蚓的長度分別為:\(3,4,2,4,(1,3)\)

\(4\)秒後:一隻長度為\(4\)的蚯蚓被切斷。\(7\)只蚯蚓的長度分別為:\(4,(1,3),3,5,2,4\)

\(5\)秒後:一隻長度為\(5\)的蚯蚓被切斷。\(8\)只蚯蚓的長度分別為:\(5,2,4,4,(1,4),3,5\)

\(6\)秒後:一隻長度為\(5\)的蚯蚓被切斷。\(9\)只蚯蚓的長度分別為:\((1,4),3,5,5,2,5,4,6\)

\(7\)秒後:一隻長度為\(6\)的蚯蚓被切斷。\(10\)只蚯蚓的長度分別為:\(2,5,4,6,6,3,6,5,(2,4)\)。所以,\(7\)秒內被切斷的蚯蚓的長度依次為\(3,4,4,4,5,5,6\)\(7\)秒後,所有蚯蚓長度從大到小排序為\(6,6,6,5,5,4,4,3,2,2\)

【樣例解釋2】

這個資料中只有\(t=2\)與上個數據不同。只需在每行都改為每兩個數輸出一個數即可。

雖然第一行最後有一個\(6\)沒有被輸出,但是第二行仍然要重新從第二個數再開始輸出。

【樣例解釋3】

這個資料中只有\(t=9\)與上個數據不同。

注意第一行沒有數要輸出,但也要輸出一個空行。

【資料範圍】

P2827

思路

如果原有的蚯蚓被切完之後直接死亡,那麼我們只需要將蚯蚓排序,每次切最長的,然後把它丟掉,再切剩下的蚯蚓裡最長的......直到蚯蚓被切完。

這是因為,每隻蚯蚓在相同時間內的生長長度是相同的,長度的變化對每隻蚯蚓是相同的,所以一次排序之後不論蚯蚓的生長速度如何,序列一定還會是有序的。

但事實上,蚯蚓被切斷之後不會死亡,而是會分裂成兩隻。我們不妨再簡化一點問題:只有較長的一節會被留下來。這樣我們難道需要把蚯蚓重新加入序列中重新排序嗎?我們不妨新開一個序列,記錄產生的新蚯蚓,會發現,越早加入這個新序列的蚯蚓會越早被再次切斷。這是因為,早進入新序列的蚯蚓較晚進入新序列的蚯蚓初始長度更長,而生長時間相同,生長長度相同。所以這個新序列也是有序的。

那如果蚯蚓分裂成了兩隻呢?我們開兩個新序列來記錄,那麼這兩個序列也一定是有序的。那麼只要我們開頭排好了序,原序列、新序列\(1\)、新序列\(2\)在接下來的操作中都會是有序的。

我們可以直接開一個佇列來維護三個序列。同時,把所有蚯蚓全部“拉長”的操作會很花時間,不妨直接記錄所有蚯蚓生長的總長度,畢竟所有蚯蚓生長速度是一樣的。然後對答案進行統計,這樣就是\(O(n\log n+m)\)的了。

AC程式碼

#include<bits/stdc++.h>
#define RG register
using namespace std;
typedef long long LL;
LL n,m,q,u,v,t,tot,a[100005],b[7100005];
queue<LL>Q1,Q2,Q3;
inline LL read()
{
    LL re=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
bool cmp(LL x,LL y){return x>y;}
int main()
{
    n=read(),m=read(),q=read(),u=read(),v=read(),t=read();
    for(RG LL i=0;i<n;i++) a[i]=read();
    sort(a,a+n);
    for(RG LL i=n-1;i>=0;i--) Q1.push(a[i]);
    for(RG LL i=1;i<=m;i++)
    {
        LL now,x=Q1.empty()?INT_MIN:Q1.front(),y=Q2.empty()?INT_MIN:Q2.front(),z=Q3.empty()?INT_MIN:Q3.front();
        if(x>=y&&x>=z) now=x,Q1.pop();
        else if(y>=x&&y>=z) now=y,Q2.pop();
        else now=z,Q3.pop();
        now+=(i-1)*q;
        LL xx=now*u/v,yy=now-xx;
        Q2.push(xx-i*q),Q3.push(yy-i*q);
        if(i%t==0) printf("%lld ",now);
    }
    puts("");
    while(!Q1.empty()) b[++tot]=Q1.front(),Q1.pop();
    while(!Q2.empty()) b[++tot]=Q2.front(),Q2.pop();
    while(!Q3.empty()) b[++tot]=Q3.front(),Q3.pop();
    sort(b+1,b+1+tot,cmp);
    for(RG LL i=t;i<=tot;i+=t) printf("%lld ",b[i]+m*q);
    return 0;
}