SweetFruits TopCoder - 12141(Matrix-Tree)

问题看起来很复杂,不可写,所以先考虑分解一下

假设最后生效的点集为$V$,那么答案只和$\sum sweetness[V_i]$和$|V|$有关

所以可以考虑对于每一种$|V|$,先预处理出方案数

得知每一种$|V|$的方案数之后,可以用$\text{meet in the middle}$法枚举得到$\sum sweetness[V_i]\leq maxsweetness$的方案数

方案数是有限制的生成树个数,所以考虑用$\text{Matrix-Tree}$求

限定有$a$个点生效,$b$个点不生效,$c$个点是$-1$

那么可能出现的边是$a-a,a-c,b-c$三种

但是我们无法保证$a$中的点一定生效,所以可以用容斥/二项式反演得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include<bits/stdc++.h>
using namespace std;

#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

#define pb push_back
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }

char IO;
int rd(){
int s=0;
int f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}

const int N=50,P=1e9+7;

ll qpow(ll x,ll k=P-2) {
ll res=1;
for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
return res;
}

int n,m;

struct Mat{
int a[N][N];
Mat(){ memset(a,0,sizeof a); }
int Det(int n){ // 用于Matrix-Tree的
static int G[N][N];
rep(i,1,n) rep(j,1,n) G[i][j]=a[i][j];
int res=1;
rep(i,1,n) {
if(!G[i][i]) rep(j,i+1,n) if(G[j][i]){ res=P-res; swap(G[i],G[j]); break; }
if(!G[i][i]) continue;
ll Inv=qpow(a[i][i]);
rep(j,i+1,n) {
ll t=a[j][i]*Inv%P;
rep(k,i,n) a[j][k]=(a[j][k]-1ll*a[i][k]*t%P+P)%P;
}
}
rep(i,1,n) res=1ll*res*a[i][i]%P;
return res;
}
};

int w[N],G[N][N],C[N][N];
void Init(){
rep(i,0,N-1) rep(j,C[i][0]=1,i) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
rep(i,0,n) {
Mat T;
rep(j,1,m) { // -1
rep(k,1,n+m) T.a[j][k]=-1;
T.a[j][j]=n+m-1;
}
rep(j,m+1,m+i) { // 生效
rep(k,1,m+i) if(j!=k) T.a[j][k]=-1;
T.a[j][j]=m+i-1;
}
rep(j,m+i+1,m+n) { // 不生效
rep(k,1,m) T.a[j][k]=-1;
T.a[j][j]=m;
}
w[i]=T.Det(n+m-1);
}
rep(i,0,n) rep(j,0,i-1) w[i]=(w[i]-1ll*C[i][j]*w[j]%P+P)%P; // 容斥/二项式反演
}

int val[N];
vector <int> st[N/2];


int Meet_In_The_Middle(int Max){
int ans=0,mid=n/2;
rep(i,0,mid) st[i].clear();
rep(S,0,(1<<mid)-1) {
int c=0,s=0;
rep(i,0,mid-1) if(S&(1<<i)) s+=val[i],c++;
if(s<=Max) st[c].pb(s);
}
rep(i,0,mid) sort(st[i].begin(),st[i].end());
rep(S,0,(1<<(n-mid))-1) {
int c=0,s=0;
rep(i,0,n-mid-1) if(S&(1<<i)) s+=val[i+mid],c++;
if(s>Max) continue;
rep(j,0,mid) {
int x=upper_bound(st[j].begin(),st[j].end(),Max-s)-st[j].begin();
if(x) ans=(ans+1ll*x*w[c+j])%P;
}
}
return ans;
}

class SweetFruits {
public:
int countTrees(vector <int> Val, int Max) {
sort(Val.begin(),Val.end(),greater <int> ());
m=0; while(Val.size() && *Val.rbegin()==-1) m++,Val.pop_back();
n=Val.size();
rep(i,0,n-1) val[i]=Val[i];
Init();
return Meet_In_The_Middle(Max);
}
};