模板 動態 DP 動態樹分治

2022-05-08 17:30:26 字數 3963 閱讀 6522

題面鏈結

#include#define ll long long

#define rg register

using namespace std;

templateinline void read(t &x)

templateinline void write(t x)

if (x < 0) x = -x, putchar('-');

int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;

for (rg int i = len; i >= 0; i--) putchar(z[i]+48);return ;

}const int n = 100010, inf = 2147483647;

struct node g[n << 1];

int last[n], gl, v[n], n;

void add(int x, int y) ;

last[x] = gl;

g[++gl] = (node) ;

last[y] = gl;

}struct matrix

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

int dfn[n], siz[n], son[n], top[n], cnt, fa[n], pos[n], ed[n];

void dfs1(int u, int ff)

}void dfs2(int u, int topf)

dfs2(son[u], topf);

ed[u] = ed[son[u]];

for (int i = last[u]; i; i = g[i].nxt)

}ll f[n][2];

void dp(int u, int ff)

return ;

}//---------------------

#define lson (o << 1)

#define rson (o << 1 | 1)

void build(int o, int l, int r) ;

return ;

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

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

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

}void modify(int o, 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[o] = t[lson] * t[rson];

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

matrix getans(int x)

void modify(int u, int w)

}//---------------------------

int main()

dfs1(1, 0); dfs2(1, 1); dp(1, 0); build(1, 1, n);

// ans = getans(1);

// printf("%lld\n", max(ans.s[0][0], ans.s[1][0]));

while (m--)

return 0;

}

%yyb

整體\(dp\)大概是對於時間建一顆線段樹,葉子節點表示在該時刻的\(dp\)答案,類似線段是分治。

對於乙個轉移,等於給區間打標記。

對於樹,我們用線段樹合併將子樹\(dp\)合併。

這個時候我們需要用一些方式維護\(dp\)。

還是用動態\(dp\)的矩陣方式維護\(dp\)

\(\beginf_&f_\end\times \beginf_&f_\\f_&-\infty\end=\beginf_&f_\end\)

但是這樣的話,矩形沒轉移一次就會遍一次,就不能快速合併兒子。

我們可以先轉一下矩陣

\(\beginf_&f_\end\times \begin0&0\\0&-\infty\end=\beginmax(f_,f_)&f_\end\)

然後\(\begin\sum max(f_,f_)&\sum f_\end\times \begin0&0\\-\infty&w\end=\beginf_&f_\end\)

這樣就可以線段樹合併求出第乙個矩陣,然後算出\(u\)的\(dp\)值。

還有乙個問題就是,區間加的線段樹怎麼合併?

其實,我們每次合併兩個節點是\(pushdown\)一下,然後如果有乙個已經是葉子了,就直接合併到另乙個上去即可。

#include#define mp make_pair

#define ll long long

using namespace std;

templatet gi()

const int n = 1e5 + 10, inf = 1e9 + 7;

int n, m;

vectore[n];

void add(int x, int y)

int rt[n], tot;

struct matrix

matrix operator * (const matrix &z) const

bool operator != (const matrix &z) const

} t[n << 6], i;

matrix mk(int x, int y) , }}; }

int ch[n << 6][2], w[n];

struct node ;

vectorq[n];

void pushdown(int o)

}int merge(int x, int y)

pushdown(x), pushdown(y);

ch[x][0] = merge(ch[x][0], ch[y][0]);

ch[x][1] = merge(ch[x][1], ch[y][1]);

return x;

}void modify(int o, int l, int r, int l, int r, int k) , }});

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

if (l <= mid) modify(ch[o][0], l, mid, l, r, k);

if (r > mid) modify(ch[o][1], mid + 1, r, l, r, k);

return ;

}void dfs(int u, int ff) , }};

for (auto v : e[u])

if (v != ff)

dfs(v, u), rt[u] = merge(rt[u], rt[v]);

for (auto i : q[u]) if (i.l <= i.r) modify(rt[u], 1, m, i.l, i.r, i.w);

t[rt[u]] = t[rt[u]] * (matrix) , }};

}void dfs2(int o, int l, int r)

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

dfs2(ch[o][0], l, mid), dfs2(ch[o][1], mid + 1, r);

}int main() );

for (int i = 1; i < n; i++) add(gi(), gi());

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

} dfs(1, 0); dfs2(rt[1], 1, m);

return 0;

}

動態樹分治

不得不說,樹結構真是巧妙神奇。因為結構簡單,所以變形剖多,在競賽中玩出的花樣也最多。動態樹分治,顧名思義,解決待修改的樹分治問題。原本的樹分治基於邊或者重心的分治可以解決大多數靜態樹鏈問題,但是待修改怎麼辦?其實很簡單,因為樹結構是不變的,就是樹分治的基礎結構不變,對於修改和詢問,只需要在第一次樹分...

模板 動態DP

點此看題 動態 dp 的思路主要是用矩陣乘法加速 dp 所以首先要知道矩陣乘法的擴充套件版 c i,k max 令人震驚的是上面這東西也滿足結合律,現在我們來證明一下,假設有三個矩陣 a,b,c 相乘,大小分別是 n times m,m times p,p times q 我們把最終某乙個位置上的值...

模板 動態規劃 數字dp

includeusing namespace std define ll long long int a 20 ll dp 20 20 可能需要的狀態1 20 可能需要的狀態2 不同題目狀態不同 ll dfs int pos,int state1 可能需要的狀態1 int state2 可能需要的狀...