题目描述
有N个点(N<=40)标记为0,1,2,…N-1,每个点i有个价值val[i],如果val[i]=-1那么这个点被定义为bad,否则如果val[i] >=0那么这个点为定义为good。现在给这N个点间连上N-1条边,使它们构成一个生成树,定义树中的点为great点当且仅当这个点本身是good点且与其相邻的点中至少有另一个good点。树的价值等于树中所有great点的价值和。定义限制价值树是指价值不大于maxVal的树,问对给定的val[]与maxVal,一共有多少种不同的限制价格树?由于答案太大,可取
modulo 1,000,000,007后的结果。
说明:两棵树是不同的,指两棵树的边集不同,注意这里的边都是无向边。
计数
注意点与点间没有区别,权值可以另外再考虑。
我们考虑g[i]表示至少i个good点不是great点,假设一共有x个good点。
那么这i个点只能向非good点连边,而剩下的可以任意连。
然后用基尔霍夫矩阵做生成树计数就是方案。
现在考虑设f[i]表示恰好i个good点不是great点,可以容斥计数。
f[i]=g[i]−∑xj=i+1Cj−ix−i∗f[j]
权值
考虑处理出h[i]表示选择i个good点使得权值和<=lim的方案。
考虑折半搜索,排序后可以two-pointer。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=40+10,maxtot=1050000,mo=1000000007;
struct dong{
int x,y;
friend bool operator <(dong a,dong b){
return a.y<b.y;
}
} e1[maxtot],e2[maxtot];
int f[maxn],g[maxn],h[maxn];
int d[maxn][maxn],a[maxn][maxn],c[maxn][maxn],cr[maxn][maxn],v[maxn],b[maxn];
int i,j,k,l,t,n,lim,m,x,top1,top2,ans,ca;
void link(int x,int y){
a[x][y]++;
a[y][x]++;
d[x][x]++;
d[y][y]++;
}
int qsm(int x,int y){
if (!y) return 1;
int t=qsm(x,y/2);
t=(ll)t*t%mo;
if (y%2) t=(ll)t*x%mo;
return t;
}
int det(){
int i,j,k,l,t=n-1,cnt=1;
fo(i,1,t){
fo(j,i+1,t){
if (!c[j][i]) continue;
l=(ll)c[i][i]*qsm(c[j][i],mo-2)%mo;
fo(k,i,n) (c[i][k]-=(ll)c[j][k]*l%mo)%=mo;
fo(k,i,n) swap(c[i][k],c[j][k]);
cnt=-cnt;
}
if (!c[i][i]) return 0;
cnt=(ll)cnt*c[i][i]%mo;
}
return cnt;
}
int calc(int y){
int i,j;
fo(i,1,n)
fo(j,1,n)
d[i][j]=a[i][j]=c[i][j]=0;
fo(i,1,y)
fo(j,x+1,n)
link(i,j);
fo(i,y+1,n)
fo(j,i+1,n)
link(i,j);
fo(i,1,n)
fo(j,1,n)
cr[i][j]=d[i][j]-a[i][j];
fo(i,1,n-1)
fo(j,1,n-1)
c[i][j]=cr[i+1][j+1];
return det();
}
void dfs(int i,int y,int cnt,int f){
if (y>lim) return;
if (!f&&i==x/2+1){
e1[++top1].x=cnt;
e1[top1].y=y;
return;
}
else if (f==1&&i==x+1){
e2[++top2].x=cnt;
e2[top2].y=y;
return;
}
dfs(i+1,y,cnt,f);
dfs(i+1,y+v[i],cnt+1,f);
}
int main(){
//freopen("apple.in","r",stdin);freopen("apple.out","w",stdout);
scanf("%d",&ca);
while (ca--){
scanf("%d%d",&n,&lim);
fo(i,1,n) scanf("%d",&v[i]);
sort(v+1,v+n+1);
reverse(v+1,v+n+1);
x=0;
fo(i,1,n)
if (v[i]==-1) break;else x++;
fo(i,0,x) g[i]=calc(i);
fo(i,0,x)
fo(j,0,x)
c[i][j]=0;
c[0][0]=1;
fo(i,1,x){
c[i][0]=1;
fo(j,1,i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
}
fd(i,x,0){
f[i]=g[i];
fo(j,i+1,x) (f[i]-=(ll)c[x-i][j-i]*f[j]%mo)%=mo;
}
reverse(f,f+x+1);
top1=top2=0;
dfs(1,0,0,0);
dfs(x/2+1,0,0,1);
sort(e1+1,e1+top1+1);
sort(e2+1,e2+top2+1);
fo(i,0,x) b[i]=h[i]=0;
fo(i,1,top2) b[e2[i].x]++;
j=top2;
fo(i,1,top1){
while (j&&e1[i].y+e2[j].y>lim){
b[e2[j].x]--;
j--;
}
fo(k,0,n-x/2) (h[e1[i].x+k]+=b[k])%=mo;
}
ans=0;
fo(i,0,x) (ans+=(ll)f[i]*h[i]%mo)%=mo;
(ans+=mo)%=mo;
printf("%d\n",ans);
}
}