题目
点这里看题目。
BZOJ 上这还是权限题。
分析
不难发现,最后我们走过的点一定组成了树上的一个连通块。
如何枚举树上一个连通块?我们可以想到用点分治。由于每一次我们进行分治之后会进行子树继续分治,这就相当于将原图变成了几个连通块。我们只需要对于每次分治,将分治中心设定为 “ 必选 ” ,然后用某种方法计算这个连通块的所有连通子块的最优贡献即可。
不难发现每个商店的物品可以拿出来做多重背包。因此我们发现可以用树形依赖的多重背包计算贡献。设状态:
f ( u , j ) f(u,j) f(u,j): u u u的子树中,花 j j j的代价可以获得的最大收益。
发现存在连通块每个点上都必须买东西的限制,因此我们必须在每个点上,先强制买一个东西之后,再进行多重背包的计算。这相当于总共可用的钱变少了。之后继续搜索,将子树上的答案合并上来。
由于本题对时间的限制不强,因此可以写多重背包二进制优化。妄图掩盖单调队列写挂的事实。
代码
#include <cstdio>
const int INF = 0x3f3f3f3f;
const int MAXN = 505, MAXM = 4005, MAXD = 105;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){
if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){
x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){
putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){
write( x / 10 ); }
putchar( x % 10 +