一道經典題目了。以前就作過(2019年山東省夏令營,2019-07-20 09:14,看來是在禮堂聽課時作的)
閒話很少說就是單調隊列,一種數據結構,維護兩個方面:
c++
爲了懶,固然用stl:deque啦(實際上是head==tail仍是head>tail搞不清楚)
git
不過用st表也能AC,須要注意卡常數,代碼以下:算法
#include<cstdio> #include<cmath> #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) int n,k,a[1000006],st[1000006][22]; inline int read()//不快讀的話過得很勉強,快讀就比較輕鬆。另外不要引用isdigit()函數,會變慢 { int x=0,f=1;char ch=getchar(); while (ch>'9'||ch<'0'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();} return x*f; } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;++i){ //scanf("%d",a+i); st[i][0]=read(); } for(int j=1,maxx;j<=20;++j){ maxx=n-(1<<j)+1; for(int i=1;i<=maxx;++i) st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]); } int len=log2(k);//直接用此函數的話,已試驗,輸入整數時無偏差 for(int i=1;i<=n-k+1;++i) printf("%d ",min(st[i][len],st[i+k-(1<<len)][len]));//實際上是(i+k-1)-(1<<len)+1 printf("\n"); for(int j=1,maxx;j<=20;++j){ maxx=n-(1<<j)+1; for(int i=1;i<=maxx;++i) st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]); } for(int i=1;i<=n-k+1;++i) printf("%d ",max(st[i][len],st[i+k-(1<<len)][len])); return 0; }
接下來是蚯蚓這道題數組
/** * 蛐蛐國裏如今共有 n 只蚯蚓( n 爲正整數)。 * 每隻蚯蚓擁有長度,咱們設第 i 只蚯蚓的長度爲 a[i] * (i=1,2,...,n) * 並保證全部的長度都是非負整數 * (即:可能存在長度爲 0 的蚯蚓)。 *//** * 每一秒,神刀手會在全部的蚯蚓中, * 準確地找到最長的那一隻(若有多個則任選一個) * 將其切成兩半。 * 神刀手切開蚯蚓的位置由常數 p * (是知足 0 < p < 1 的有理數) * 決定,設這隻蚯蚓長度爲 x, * 神刀手會將其切成兩隻長度分別爲 * ⌊px⌋ 和 x - ⌊px⌋ 的蚯蚓。 * 特殊地,若是這兩個數的其中一個等於 0, * 則這個長度爲 0 的蚯蚓也會被保留。 * 此外,除了剛剛產生的兩隻新蚯蚓, * 其他蚯蚓的長度都會增長 q(是一個非負整常數)。 */
附上ckw大佬的分析(也是2019年夏令營的)
數據結構
那麼思想的話已經很明顯了。正解也不少,文章最後貼一個正解作法(咱們教練寫的)。這裏給一個騙分的作法:函數
咱們快速寫一個不是正解可是必定對的代碼而後瘋狂卡常,以下:
考慮到每次取最大蚯蚓,且不斷插入/刪除,咱們用一個優先隊列來維護
其餘的就是照着題面模擬了。須要注意的是每次插入新的蚯蚓時要減去time×q以「拉平」,
換言之,其餘蚯蚓都少加了time×q,切的這條蚯蚓切的時候是真實的長度,切後要變回「虛假」的長度
其餘的按照體面輸入輸出就完了。開不開O2優化都是85分,優化做用也有限。優化
#include<cstdio> #include<queue> using namespace std; int _X,_F,CH; inline int read(){//read _X=0;_F=1;CH=getchar(); while(CH>57||CH<48){if(CH=='-')_F=-1;CH=getchar();} while(CH>=48&&CH<=57){_X=_X*10+CH-48;CH=getchar();} return _X*_F; } priority_queue<int/*,vector<int>,greater<int> */>pq; int n,m,q,u,v,t/*,a[100005]*/;//含義見題目 int longest,shorter;//變量名起的長一點便於理解 int main(){ n=read();m=read();q=read();u=read();v=read();t=read(); for(int i=1;i<=n;++i) { //a[i]=read(); pq.push(read()); } for(int time=1;time<=m;++time) { longest=pq.top();pq.pop(); longest+=(time-1)*q;//本輪他並無變長 if(time%t==0)printf("%d ",longest); shorter=longest*(long long)u/v;//這個long long常數很大,可是必須 pq.push(shorter-time*q);pq.push(longest-shorter-time*q); //寫這篇文章時我想到了把time*q存起來,可是該拿的85分到手了,剩下幾個點真的沒什麼用 } putchar('\n'); int addlen=m*q;//<2^31 for(int i=1;i<=n+m;++i) { if(i%t){pq.pop();continue;} longest=pq.top();pq.pop(); printf("%d ",longest+addlen); } return 0; }
這是一份正解,三個單調隊列,含義如ckw所述。題解滿大街都是,貼個咱們教練的:spa
# include<bits/stdc++.h> #define rep(i,n) for(int i=1;i<=n;++i) using namespace std; inline int read() { int x=0,f=1; char ch;ch=getchar(); while(ch<48 ||ch>57){if(ch=='-') f=-1;ch=getchar(); } while(ch>=48&&ch<=57) {x=x*10+ch-48;ch=getchar();} return x*f; } const int N=1e5+5; const int M=1e7; int a[3][M],cut[M],h[3],t[3];//a[0] 原數列 a[1] 砍斷後長的段 a[2]砍斷後短的段 // cut [] 記錄被砍斷的蚯蚓 h[0] h[1] h[2] 表明 a[0] a[1] a[2]頭指針 t[]分別是尾指針 int n,m,q,u,v,T,inf; bool cmp(int &a,int &b) {return a>b;} int main() { //freopen("in.txt","r",stdin); cin>>n>>m>>q>>u>>v>>T; for(int i=0;i<=2;++i) rep(j,M)a[i][j]=-1e9; inf=-1e9; h[0]=h[1]=h[2]=1; cut[0]=0; rep(i,n) a[0][i]=read(); sort(a[0]+1,a[0]+1+n,cmp);// 首先讓a[0] 從大到小 有序 double per=u*1.0/v; rep(i,m)// 開始砍蚯蚓 { int temp,maxn; if(a[0][h[0]]>=a[1][h[1]]) maxn=a[0][h[0]],temp=0; else maxn=a[1][h[1]],temp=1; if(a[2][h[2]]>maxn) maxn=a[2][h[2]],temp=2; // 找到a[0] a[1] a[2] 隊首最大的 就是 應該被砍的蚯蚓 h[temp]++;//被砍的蚯蚓被彈出隊首 if(i%T==0)cut[++cut[0]]=maxn+(i-1)*q;//被砍的蚯蚓應當加上增加補償,每一秒都增加q // 接下來把砍斷的蚯蚓 按大小存在a[1] a[2] 隊列 int small=(maxn+(i-1)*q)*per,big=maxn+(i-1)*q-small; if(big<small) swap(big,small); a[1][++t[1]]=big-i*q,a[2][++t[2]]=small-i*q; } //按要求輸出 過後我想cut數組也許沒有必要 直接輸出便可 rep(i,cut[0]) cout<<cut[i]<<" "; cout<<endl; int left=(n+m)/T,cnt=0; // 後來身負洪荒之力人來了 蚯蚓不在增加 只須要把序列輸出便可 while(left) { int temp,maxn=inf; if(a[0][h[0]]>=a[1][h[1]]) maxn=a[0][h[0]],temp=0; else maxn=a[1][h[1]],temp=1; if(a[2][h[2]]>=maxn) maxn=a[2][h[2]],temp=2; h[temp]++; if(++cnt%T==0)cout<<maxn+m*q<<" ",left--; } return 0; }
還有一位同窗因常數過大被卡因此必定快讀:
指針
文末總結:
一些看似高級的題目其實可能有簡單(嗎?)的作法,尤爲是單調隊列,好幾回出現了
因此仍是那句話,基礎算法靈活掌握。
另外就是ckw教的經驗吧,先考慮部分分的限制條件,不行就先本身加一個,在這種受限的背景下題目還會含有什麼限制條件,
再推廣到更高分看是否是還成立。我以爲部分分就挺香的了code