蓝桥杯 - 试题 J: 砍竹子(双向链表+堆/思维)
題目大意:給出一排 nnn 個竹子的高度,每次操作可以選擇連續的,高度相同的竹子,使其高度變為 ??H2+1??\lfloor \sqrt{\lfloor \frac{H}{2}+1\rfloor} \rfloor??2H?+1???,問最少需要執行多少次操作可以使得所有竹子都變成 111
題目分析:最樸素的思路就是去模擬,賽時想到的也是這個思路,但是沒時間去寫了,就打了個 O(n2logn)O(n^2logn)O(n2logn) 的暴力騙了點分
首先需要知道每次操作的一個性質,根號和除以二相結合,使得 1e181e181e18 的數字沒有幾次就會下降成 111,這里視為 logloglog 的復雜度是完全可以的
那么模擬的思路就呼之欲出了:
賽時感覺需要用線段樹或平衡樹來維護,沒有多想,賽后經過帽帽鴿的提醒,發現這個模型可以套用雙向鏈表+堆的思路去實現。簡單來說就是對序列維護一個雙向鏈表,對于需要合并的一段,壓縮成一個點,扔進堆里每次選擇最大值即可,時間復雜度 O(nlog2n)O(nlog^2n)O(nlog2n)
賽后和楊大佬討論之后發現了另一個思路,對于一個位置 iii 來說,合并帶來最直接的影響就是將 i?1i-1i?1、iii、以及 i+1i+1i+1 視為了一個整體,之后的操作實質上也完全可以視為一個整體了。所以我們不妨對于任意一個位置 iii 來說,只關注其兩個狀態:
如何實現呢?因為每個數字下降為 111 的過程必定會產生一個序列,我們只需要關注 i?1i-1i?1 的序列和 iii 的序列,最長公共后綴的長度就好了,妙啊
時間復雜度還是 O(nlog2n)O(nlog^2n)O(nlog2n)
代碼:
雙向鏈表+堆
思維
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=1e6+100; vector<LL>node[N]; int main() {int n;cin>>n;for(int i=1;i<=n;i++) {LL x;scanf("%lld",&x);while(x>1) {node[i].push_back(x);x=sqrt(x/2+1);}reverse(node[i].begin(),node[i].end());}int ans=0;for(int i=1;i<=n;i++) {int len=(int)min(node[i].size(),node[i-1].size());for(int j=0;j<len;j++) {if(node[i][j]!=node[i-1][j]) {len=j;break;}}ans+=(int)node[i].size()-len;}cout<<ans<<endl;return 0; }總結
以上是生活随笔為你收集整理的蓝桥杯 - 试题 J: 砍竹子(双向链表+堆/思维)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021ICPC(澳门) - LCS S
- 下一篇: 蓝桥杯 - 试题 H: 扫雷(思维)