例题
技巧
点数-边数=1
现在如果你要在树上统计某种特定集合的数量,有一种比较简单的方法就是统计包括某个点的集合数量,加起来,减去包括某条边的集合,就可以得出最后的集合了。
DFS序转移
如果要求你在树上背包你打算怎么办,神犇就给出了一个很好的方法。
一个集合利用合并从下到上做背包是 O ( n 3 ) O(n^3) O(n3),但是遍历一个个点加入进来就是 O ( n 2 ) O(n^2) O(n2)了,但是关键是如何遍历。
先把DFS序(进入的顺序)做出来,然后倒着DFS队列DP,如果一个点 x x x选择自己的话,他就选择 + 1 +1 +1的位置,如果他不选择自己的话,那么他就直接继承 + s i z e [ x ] +size[x] +size[x]的位置,就可以了。
题解
学会了技巧,这道题目就简单多了,模数是 5 23 5^{23} 523,首先利用DFS序DP得到最大的价值,同时得到包括某个点的最大价值的集合数,用组合数取模(之前的同余系列)得到包括这个点的完美集合,最后减去包括某条边的完美集合即可。(当然在DFS序DP之前要先处理那些点能否到达,这样那个重量<=的条件好处理)
另外,最大价值的集合数最大大概为 2 60 2^{60} 260。
时间复杂度: O ( n 2 m + 取 模 复 杂 度 ) O(n^2m+取模复杂度) O(n2m+取模复杂度)
代码
#include<bits/stdc++.h>
#define S 23
#define N 70
#define NN 140
#define LLp pair<LL,LL>
using namespace std;
typedef long long LL;
template <class T>
inline T mymax(T x,T y){
return x>y?x:y;}
template <class T>
inline T mymin(T x,T y){
return x<y?x:y;}
LL mod=(LL)11920928955078125;
inline LL mul(LL x,LL y){
return (x*y-(LL)((long double)x*y/mod+1e-10)*mod);}
inline LL pow(LL x,LL k)//求逆元专用
{
LL ans=1;
while(k)
{
if(k&1)ans=mul(x,ans);
x=mul(x,x);k>>=1;
}
return ans;
}
namespace Big_num//大整数组合数
{
LL pw[S]={
1},C[S]