題面鏈結
#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 可能需要的狀...