NOIP2024模擬12:孤帆遠影
聽了機房同學的討論,於是T1死磕冒泡和逆序對做法。最後只得了40pts。
思想對了,但不是自己的做法。
還是要堅持自己想,堅持自己可以想出來,不要被任何人帶偏。
T1
-
一句話題意:將一個已知序列透過不斷“交換相鄰位置”的操作調整成不嚴格單峰狀態,問最小的操作次數。
-
有一種猜想是隻要欽定了峰頂的位置,那麼左右兩邊是不會交叉的。
- 但這個猜想是錯誤的,它的證偽可以透過正解來理解
-
正解:假設題目要求我們調成升序,那麼答案就是逆序對的數量。
-
現在是什麼呢?要求前半段升序後半段降序。
-
那就分開逆序對!
-
具體來說,對於第 \(i\) 個數,想要待在左區間, 就必須穿過左邊比它大的每個數,即在它左邊的逆序對數量,待在右區間同理.
-
由於峰頂的位置不做限制,所以我們只需要看每個數放左邊移動步數少一點,還是放右邊少一點,就行了.
-
用樹狀陣列求逆序對即可,只不過是正著倒著各掃一遍.
-
所以此題我很早就陷入了一個誤區:列舉峰頂的位置,想來這其實不是題目所求.把自己限制住了!
- 下次考試應該先在草稿本上寫出這個想法.嘗試一段時間返回去檢查自己的思想是不是除了問題的時候,就方便大膽地走出誤區.
時間複雜度 \(O(N log N)\)
#include<bits/stdc++.h> #define F(i,l,r) for(int i(l);i<=r;++i) #define G(i,r,l) for(int i(r);i>=l;--i) #define int long long #define lowbit(x) (-x&x) using namespace std; using ll = long long; const int N=2e5+5; int n,pos=0,mx=0; ll L[N],R[N],ans=0; ll a[N],tr[N]; void add(int x){ for(;x<=mx;x+=lowbit(x)) tr[x]++; } int ask(int x){ int res=0; for(;x>=1;x-=lowbit(x)) res+=tr[x]; return res; } signed main(){ //freopen("inde.in","r",stdin); //freopen("inde.out","w",stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n; F(i,1,n) cin>>a[i],mx=max(mx,a[i]); F(i,1,n){ add(a[i]); L[i]=i-ask(a[i]); }memset(tr,0,sizeof(tr)); G(i,n,1){ add(a[i]); R[i]=n-i+1-ask(a[i]); } F(i,1,n){//the fjx ans+=min(L[i],R[i]); } cout<<ans; return 0; }
T2
-
一句話題意:給定一個起點,你現在需要依次抵達 \(n\) 個目標區間,既可以親自去,也可以請別人代勞,但花費都是起終點之間的距離(允許親自走一半再代勞一半).問最少的花費.
-
有點兒抽象,還沒有完全理解,嘗試著解釋一下:
-
記 \(f[i][j]\) 表示第 \(i\) 次遊歷最終到達 \(j\) 的最小花費.
- 首先從 \(f[i-1]\) 繼承dp值
- 對於操作1: \(j\) 離 \([l_i,r_i]\) 的最近距離即為此部分貢獻.
- 對於操作2:用 \(f[i][j]+1\) 更新 \(f[i][j-1]\) 和 \(f[i][j+1]\)
-
最關鍵的一步:根據操作2,對於每個 \(i\), 將 \(f[i][j]\) 看成關於 \(j\) 的函式,則一定長這個樣子:
-
轉移時,維護中間平的那一段,最後得到的貢獻一定就是最優的.(感性理解一下)
-
時間複雜度 \(O(N)\)
#include<bits/stdc++.h> #define F(i,l,r) for(int i(l);i<=r;++i) #define G(i,r,l) for(int i(r);i>=l;--i) #define int long long using namespace std; using ll = long long; const int N=5e5+105; int n,x; signed main(){ // freopen("festival.in","r",stdin); // freopen("festival.out","w",stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n>>x; int l=x,r=x,L,R; ll ans=0; while(n--){ cin>>L>>R; if(l<=R && L<=r){ if(L>l) l=L; if(R<r) r=R; } if(R<l) ans+=l-R,r=l,l=R; if(L>r) ans+=L-r,l=r,r=L; } cout<<ans; return 0; }
T3
-
\(O(NlogN)\) 求\(LIS\) 的板題,只不過帶了個係數而已.
-
理解:\(C_i\)的係數隻影響後續 \(C_{i+1}\) 的判斷,而不影響當前判斷.
-
唯一的細節就是帶係數之後的數不一定比原來小,要取 min.
#include<bits/stdc++.h> #define F(i,l,r) for(int i(l);i<=r;++i) #define G(i,r,l) for(int i(r);i>=l;--i) #define lowbit(x) (-x&x) #define int long long using namespace std; using ll = long long; const int N=1e6+5; int f[N],a[N],b[N]; int n; signed main(){ //freopen("geranium.in","r",stdin); //freopen("geranium.out","w",stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n; F(i,1,n) cin>>a[i]; F(i,1,n) cin>>b[i]; int ans=0; F(i,1,n) f[i]=2e18; F(i,1,n){ int pos=lower_bound(f+1,f+ans+1,a[i])-f; f[pos]=min(f[pos],a[i]*b[pos]); ans=max(ans,pos); } cout<<ans<<"\n"; return 0; }