神仙DP 單調佇列 模擬題 區間覆蓋

2022-04-16 22:16:14 字數 3236 閱讀 3526

傳送門

給出數軸上的n個線段,保留最多k條線段,問這些被保留下來的線段的並集長度為最多為多少。

第一行兩個數n和k

接下來n行,每行兩個數,表示一條線段的左右端點。(0<=每個數<109)

乙個數表示答案。

321

87152

14

12
對於30%的資料,1<=n<=20

對於60%的資料,1<=n<=300

對於100%的資料,1<=n<=100000,,1<=k<=min(100,n);

想了半天,看這個資料範圍大概是nlogn可過,n*k也可過。先考慮貪心,貌似可行,但是貪心的複雜的是o(kn2l

ogn)……手動再見還不如樸素dp。

考慮dp。樸素dp十分好想,按區間右端點排序,設f[i][j]為前i個線段選j個,其中必選i。這樣易得轉移方程:

f[i][j]=max(max,max)。

直觀上,方程表示從前h個選j-1個開始轉移,按區間是否有交劃分轉移。

如此轉移,需要列舉當前區間,當前區間前的區間,以及線段個數,時間複雜度為o(n2k)。期望得分50分。

下面我進行了許多毫無卵用的優化:

觀察前半個轉移方程,需要找到前h個中最大的f[h][j-1]。顯然線段樹可以儲存區間最大值,於是對於前半段線段樹優化。這樣就無需列舉h。那麼如何確定h呢?考慮因為右端點滿足單調性,於是進行二分。二分h的位置。複雜度o(logn)。對於後半個方程,想破腦袋也只能列舉轉移,這樣在期望狀態下,複雜度為o(nklog2n)。兩個log乙個是線段樹的乙個是二分的。於是我們發現,我們的期望得分仍然是50分……

這是跑的最快的50分做法。差點卡常卡過70分。

下面是正解:

對於前半部分方程,需要取前i個的最大值,顯然字首最大值可做。即max[i][j]=max(max[i-1][j],frog[i][j])這樣,對於前半部分的轉移,轉移時間從o(logn)降低到了o(1)。可是這樣期望還是50分啊。考慮最大h的位置。由於滿足右端點遞增,如果在排序是將左端點為第二關鍵字公升序排序,那麼顯然h是遞增的。因為h代表的是右端點小於等於l[i]的右端點區間最大的編號。由於r[i]遞增,l[i]公升序,所以l[i]遞增。(對於相互包含的區間,顯然大區間優於小區間,需要去重)這樣只需要不斷++h,判斷h是否合法即可。這樣,決策時間也降到了o(1)。下面考慮對於後半部分方程的轉移。由於r[i]、l[i]遞增,所以滿足第二部分方程的h是遞增的。如此可以進行單調佇列優化。對於合法的但是小的f,會被新進佇列的元素直接覆蓋。不合法的會從隊首彈出。如此,整個轉移的複雜度被降到了o(1)。所以本題的複雜度為θ(nk+n+nlogn),其中nk為dp,n為單調佇列總維護次數,nlogn為排序複雜的。即o(nk)。這樣複雜度就合法通過了本題。

#include#include

#define maxn 100010inline

void qr(int &x)

while(ch>='

0'&&ch<='

9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();

if(f) x=-x;

}inline

int max(const

int &a,const

int &b)

inline

int min(const

int &a,const

int &b)

inline

int abs(const

int &x)

inline

void swap(int &a,int &b)

intn,k,ans;

struct

m ;m mu[maxn];

int frog[maxn][110

];inline

bool cmp(const m &a,const m &b)

intmain()

std::sort(mu+1,mu+1+n,cmp);

for(int i=1;i<=n;++i)

}ans=max(ans,frog[i][k]);

}printf(

"%d\n

",ans);

return0;

}

50分無優化

#include#include

#include

#include

#include

#include

#include

#include

using

namespace

std;

typedef

long

long

ll;typedef

long

double

ld;typedef pair

pr;const

double pi=acos(-1

);#define rep(i,a,n) for(int i=a;i<=n;i++)

#define per(i,n,a) for(int i=n;i>=a;i--)

#define rep(i,u) for(int i=head[u];i;i=next[i])

#define clr(a) memset(a,0,sizeof a)

#define pb push_back

#define mp make_pairld eps=1e-9

;ll pp=1000000007

;ll mo(ll a,ll pp)

ll powmod(ll a,ll b,ll pp)

ll read()

//head

#define n 110000ll f[

2][n];

pr a[n];

intn,k,nn,q[n];

ll b[n],c[n];

intmain()

rep(j,

0,n)f[0][j]=f[1][j],f[1][j]=-1000000

; }

printf(

"%d\n

",ans);

return0;

}

1、在需要前i個元素中最大值且i轉移單調時,考慮字首最大值而不是線段樹。這樣複雜度可以下降為o(1)。其實我就是資料結構學傻了

2、轉移區間滿足單調性時,考慮單調佇列優化。需要注意的是,單調佇列中單調的是元素下標而不是dp值。這不是廢話嗎

3、神仙題優化時可以畫圖考慮轉移位置,從而發現性質。

概率dp 神仙題

題目大致意思 總共有n種郵票,每選一張郵票,郵票的 會加一,郵票的初始值是1,最後問拿完n種郵票的期望 思路 發表一下看這個題題解的感想 這個題太仙了,概率dp的套路司空見慣就是把陣列定義成已經考慮前i個 到n的期望。那麼我們先開乙個陣列來記錄,f記錄從已經選了i個從第i種開始選到第n種的期望步數,...

NOIP模擬 最佳序列(單調佇列DP)

給乙個序列,求所有滿足長度在給定 l,r 之中的序列的平均值的最大值。題解 單調佇列 dp。看到平均值應該想到二分平均值然後再每個數減去這個平均值,此時所有平均值滿足大等於當前二分的平均值的序列的區間和大等於0。考慮統計減去平均值後序列的字首和,問題變為每個數ai 選擇之前 i r,i l 的乙個數...

單調佇列 DP

烽火傳遞 description 烽火台又稱烽燧,是重要的防禦設施,一般建在險要處或交通要道上。一旦有敵情發生,白天燃燒柴草,通過濃煙表達資訊 夜晚燃燒乾柴,以火光傳遞軍情。在某兩座城市之間有n個烽火台,每個烽火台發出訊號都有一定的代價。為了使情報準確的傳遞,在m個烽火台中至少要有乙個發出訊號。現輸...