簡化版看了簡化版之後容易想到乙個dp。
$dp_$代表$u$的子樹內不選$u$的最大答案。
$dp_$代表$u$的子樹內選$u$的最大答案。
有轉移方程:
$dp_=\sum_ max(dp_, dp_)$。
$dp_=(\sum_ dp_)+a_u$。
然後對於ddp的模板題我們就得到了乙個$o(nm)$的優秀演算法。
接下來我們就來看看怎麼用ddp的套路來切掉這道題。
因為ddp只是利用樹剖把樹剖成一條條鏈,然後再鏈上做ddp,所以我們要有乙個適應鏈dp的方程和狀態。
我們在樹剖時只能處理重邊的結果,不能處理輕邊的結果,所以我們要先把輕邊的結果算出來。
設$u$的重兒子為$son(u)$。
因為是在重鏈上序列dp,所以我們要先把重兒子分離出來。
令$g_=\sum_ max(dp_,dp_)$,$g_=(\sum_ dp_) + a_u$。
則有$dp_=g_+max(dp_, dp_)$,$dp_=g_+dp_$。
我們發現能做ddp還有乙個前提是變成序列dp後可以用廣義矩乘來表示轉移,比如上面的例子可以表示成:
$\left[ \begin g_ & g_ \\ g_ & -inf \end \right] \times \left[ \begin dp_ \\ dp_ \end \right]=\left[ \begin dp_ \\ dp_ \end \right] $
然後我們就可以愉快的ddp了!要想查任意點的dp值只需查從它這個點到它所在的重鏈的結尾上的點的矩陣乘積即可(所以我們還需維護鏈尾),因為鏈尾的矩陣即為其dp值矩陣。
而這個$g$陣列和$dp$陣列都是可以在樹鏈剖分時預處理出來的。
修改我們發現我們只需修改$g$的值,即矩陣即可。
對於乙個位置先把它的修改了,然後跳到它的鏈頭的父親,繼續迴圈做直到到根。
為什麼可以這麼做因為$g$是與重鏈無關的,只用修改輕鏈的情況。
具體修改時需要記錄前乙個點(即前一條重鏈的聯投)的dp值修改前後的增量,然後用之前的值加上其即可(因為是修改乙個輕兒子)。
具體可看**。
查詢查詢時查根的dp值即可。
#include #include#include
using
namespace
std;
const
int n = 100010;//
輸入輸出
template void read(t &x)
template
void
write(t x)
template
void
print(t x)
template
void cmax(t &x, t y)
template
void cmin(t &x, t y)
int n;//
節點個數
int m;//
操作個數
int a[n];//
初始值
int dp[n][2];//
dp陣列
//前向星
namespace
qxxedge[n
<< 1
];
inthead[n], tot;
inline
void add(int u, int v)
}using
namespace
qxx;
//矩陣
namespace
matrixa[n];
inline
void init(matrix &x)
inline matrix mul(matrix x, matrix y)
}using
namespace
matrix;
//樹鏈剖分
namespace
tcp }
void dfs2(int x, int chain)
dp[x][
0] += a[x].arr[1][1
]; dp[x][
1] += a[x].arr[2][1
]; }
}using
namespace
tcp;
namespace
segment_treetr[n
<< 2
]; inline
void push_up(int p)
void build(int p, int l, int
r)
int mid = (l + r) >> 1
; build(p
<< 1
, l, mid);
build(p
<< 1 | 1, mid + 1
, r);
push_up(p);
}void change(int p, int l, int r, int
pos)
int mid = (l + r) >> 1
;
if (pos <= mid) change(p << 1
, l, mid, pos);
else change(p << 1 | 1, mid + 1
, r, pos);
push_up(p);
}matrix query(
int p, int l, int r, int l, int
r)
int mid = (l + r) >> 1
;
if (r <= mid) return query(p << 1
, l, mid, l, r);
else
if (l > mid) return query(p << 1 | 1, mid + 1
, r, l, r);
else
return mul(query(p << 1, l, mid, l, mid), query(p << 1 | 1, mid + 1, r, mid + 1
, r));
}}using
namespace
segment_tree;
intmain()
dfs1(1);
dfs2(
1, 1
); build(
1, 1
, n);
while (m--)
matrix ans = query(1, 1, n, top[1], end[top[1
]]);
print(max(ans.arr[
1][1], ans.arr[2][1
]));
}return0;
}
沒有上司的舞會
題目描述 description ural大學有n個職員,編號為1 n。他們有從屬關係,也就是說他們的關係就像一棵以校長為根的樹,父結點就是子結點的直接上司。每個職員有乙個快樂指數。現在有個周年慶宴會,要求與會職員的快樂指數最大。但是,沒有職員願和直接上司一起與會。輸入描述 input descri...
沒有上司的舞會
ural大學有 n 個職員,編號為 1 n 他們有從屬關係,也就是說他們的關係就像一棵以校長為根的樹,父結點就是子結點的直接上司。每個職員有乙個快樂指數。現在有個周年慶宴會,要求與會職員的快樂指數最大。但是,沒有職員願和直接上司一起與會。第一行乙個整數n。1 n 6000 接下來n行,第i 1行表示...
沒有上司的舞會
某大學有n個職員,編號為1 n。他們之間有從屬關係,也就是說他們的關係就像一棵以校長為根的樹,父結點就是子結點的直接上司。現在有個周年慶宴會,宴會每邀請來乙個職員都會增加一定的快樂指數ri,但是呢,如果某個職員的上司來參加舞會了,那麼這個職員就無論如何也不肯來參加舞會了。所以,請你程式設計計算,邀請...