複習 動態dp

2022-03-19 21:09:42 字數 2876 閱讀 6496

你還是可以認為我原來寫的動態dp就是在扯蛋。

首先作為乙個\(dp\)題,我們顯然可以每次修改之後都進行暴力\(dp\),設\(f[i][0/1]\)表示當前考慮\(i\)及其子樹內的點,當前這個點是選還是不選時能夠得到的最大權值,那麼我們可以得到轉移:\(f[i][0]+=\max\,f[i][1]+=f[v][0]\),其中\(v\)是\(i\)的乙個兒子。

那麼這樣子的複雜度就是\(o(qn)\)。

仔細想想我們這樣子為什麼慢?因為我們重複計算了大量相同的轉移,那麼我們發現我們可以每次只需要修改當前的修改點到達根節點的這條鏈的所有點的\(dp\)值就好了,然而這樣子複雜度最差還是\(o(nq)\)的。

那麼我們還有**慢呢?

仔細想想,我們的轉移都是一模一樣的,區別只是在於每次的取值不同而已。

先考慮一模一樣的轉移可以怎麼處理,似乎可以矩陣乘法?那麼轉移可以寫成:

\[\begin0&0\\v_u&-\infty\end\times \beginf_\\f_\end=\beginf_\\f_\end

\]然而這樣子的轉移只有在這個點轉移第乙個兒子的時候是對的。

如果有多個兒子的話,我們把\(v_u\)看成除了這個兒子之外的\(f[u][1]\),\(0\)看成除了這個兒子之外的\(f[u][0]\)。

那麼等價於是我們要把兒子給分成兩個部分,第一部分表示的是這個特殊轉移的兒子,第二部分是剩下的兒子。似乎可以重鏈剖分?

那麼對於重兒子我們可以直接維護這個矩陣,這樣子用線段樹維護矩陣乘法就可以算出重兒子部分的貢獻,其他輕兒子暴力往上貢獻。

看起來這個東西複雜度就很對了啊,單次修改只會造成\(log\)次暴力修改。

#include#includeusing namespace std;

#define ll long long

#define max 100100

#define lson (now<<1)

#define rson (now<<1|1)

const ll inf=1e17;

inline int read()

struct linee[max<<1];

int h[max],cnt=1;

inline void add(int u,int v);h[u]=cnt++;}

int n,q,v[max];

int fa[max],top[max],bot[max],size[max],hson[max],dfn[max],tim,ln[max];

void dfs1(int u,int ff)

}void dfs2(int u,int tp)

ll f[max][2];

void dfs(int u,int ff)

}struct matrix

}t[max<<2],tmp[max];

matrix operator*(matrix a,matrix b)

void build(int now,int l,int r)

; return;

} int mid=(l+r)>>1;

build(lson,l,mid);build(rson,mid+1,r);

t[now]=t[lson]*t[rson];

}void modify(int now,int l,int r,int p)

int mid=(l+r)>>1;

if(p<=mid)modify(lson,l,mid,p);

else modify(rson,mid+1,r,p);

t[now]=t[lson]*t[rson];

}matrix query(int now,int l,int r,int l,int r)

void modify(int u,int w)

}int main()

struct linee[max<<1];

int h[max],cnt=1;

inline void add(int u,int v);h[u]=cnt++;}

int n,q;ll v[max];

int fa[max],top[max],bot[max],size[max],hson[max],dfn[max],tim,ln[max];

void dfs1(int u,int ff)

}void dfs2(int u,int tp)

ll f[max][2];

void dfs(int u,int ff)

}struct matrix

}t[max<<2],tmp[max];

matrix operator*(matrix a,matrix b)

void build(int now,int l,int r)

; return;

} int mid=(l+r)>>1;

build(lson,l,mid);build(rson,mid+1,r);

t[now]=t[lson]*t[rson];

}void modify(int now,int l,int r,int p)

int mid=(l+r)>>1;

if(p<=mid)modify(lson,l,mid,p);

else modify(rson,mid+1,r,p);

t[now]=t[lson]*t[rson];

}matrix query(int now,int l,int r,int l,int r)

void modify(int u,ll w)

}int main()

{ n=read();q=read();scanf("%*s");ll sum=0;

for(int i=1;i<=n;++i)sum+=(v[i]=read());

for(int i=1,u,v;i戳這裡

數字dp複習

今天上午複習了數字dp 之前也沒學多少 想把dp大類都學一些然後開始刷dp類題目 然而發現之前不是真的明白,這次感覺是差不多明白了 尤其是那道不要62 之前以為明白了,實際上不明白,尤其是if limit 的時候才能記錄,其實很容易理解的,每次跑dfs遞迴的時候,都是深度搜尋,每乙個數向下都會搜到最...

DP 套 DP 與動態 DP

即對於某種過程,我們需要 dp 進行,如果我們要對這個 dp 過程還要計數或者做其他的事,這時候我們需要對每一種 dp 的狀態都記錄下其對應的值,作為計數 dp 的狀態。例題 cf578d cf979e cf1229e2 對於某種 dp 過程,我們可能需要修改這個 dp 中間的某些常數。通常情況下這...

動態規劃 dp

威威貓系列故事 打地鼠 威威貓最近不務正業,每天沉迷於遊戲 打地鼠 每當朋友們勸他別太著迷遊戲,應該好好工作的時候,他總是說,我是威威貓,貓打老鼠就是我的工作!無話可說.我們知道,打地鼠是一款經典小遊戲,規則很簡單 每隔乙個時間段就會從地下冒出乙隻或多隻地鼠,玩遊戲的人要做的就是打地鼠。假設 1 每...