一个good点如果只和bad相连就不计算他的价值,否则算入总价值记作great点,要求总价值<=maxval
这些good点之间除了权值没啥区别,生成树计数容易想到用矩阵树定理去统计,但是用矩阵树定理,只能通过让他们之和bad间有边限制i个点一定不是great点,对于其他点有可能还有不是great点的情况统计
于是用g[i]表示至少i个good点非great,f[i]表示恰好i个good点非great,用减掉恰好j(j>i)个good点非great的情况,考虑我们固定了1~i非great,共m个good点,剩下j-i个非great点在m-i个里面有
Cj−im−i
种情况,每种对应一个f[j],有
f[i]=g[i]−∑mj=i+1Cj−im−if[j]
值非常大不能dp,所以统计sum<=maxval的great情况貌似NPC?
要2^n枚举,直接枚举不能接受,考虑折半,枚举哪些点是great,i个great点的情况枚举出来后排序,然后meet in middle
复杂度 O(n4+2n/2nlogn)
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 42;
const int Mod = 1e9+7;
inline void add(int &x,const int &y){x+=y;if(x>=Mod)x-=Mod;}
inline void dec(int &x,const int &y){x-=y;if(x<0)x+=Mod;}
int pw(int x,int k)
{
if(x<0) x+=Mod;
int re=1;
for(;k;k>>=1,x=(ll)x*x%Mod) if(k&1)
re=(ll)re*x%Mod;
return re;
}
int inv(int x){return pw(x,Mod-2);}
int n,m,N,val;
int c[maxn][maxn],v[maxn];
struct matrix{int a[maxn];}a[maxn];
void Gauss()
{
for(int i=1;i<N;i++)
{
if(a[i].a[i]==0)
{
int flag=-1;
for(int j=i+1;j<N;j++) if(a[j].a[i]) { flag=j;break; }
if(flag!=-1) for(int j=1;j<N;j++) swap(a[i].a[j],a[flag].a[j]);
}
if(a[i].a[i]==0) return;
int cc=inv(a[i].a[i]);
for(int j=i+1;j<N;j++) if(a[j].a[i])
{
int div=(ll)cc*a[j].a[i]%Mod;
for(int k=i;k<N;k++) dec(a[j].a[k],(ll)a[i].a[k]*div%Mod);
}
}
}
int g[maxn],f[maxn];
void cal(const int k)
{
for(int i=1;i<=N;i++) memset(a[i].a,0,sizeof a[i].a);
for(int i=1;i<=k;i++)
{
for(int j=1;j<=n;j++)
{
int id=m+j;
a[i].a[i]++,a[id].a[id]++;
a[i].a[id]--,a[id].a[i]--;
}
}
for(int i=k+1;i<N;i++)
{
for(int j=i+1;j<=N;j++)
{
a[i].a[i]++,a[j].a[j]++;
a[i].a[j]--,a[j].a[i]--;
}
}
for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) dec(a[i].a[j],0);
Gauss();
g[k]=1;
for(int i=1;i<N;i++) g[k]=(ll)g[k]*a[i].a[i]%Mod;
}
vector<int>V1[maxn],V2[maxn];
void search()
{
int mid=N>>1;
int al=1<<mid;
for(int i=0;i<=mid;i++) V1[i].clear();
for(int i=0;i<al;i++)
{
int num=0,sum=0;
for(int j=1;j<=mid;j++) if(i>>j-1&1)
{
if(v[j]==-1) { num=-1; break; }
num++,sum+=v[j];
}
if(num!=-1) V1[num].push_back(sum);
}
for(int i=0;i<=mid;i++) sort(V1[i].begin(),V1[i].end());
al=1<<N-mid;
for(int i=0;i<=N-mid;i++) V2[i].clear();
for(int i=0;i<al;i++)
{
int num=0,sum=0;
for(int j=1;j<=N-mid;j++) if(i>>j-1&1)
{
if(v[mid+j]==-1) { num=-1; break; }
num++,sum+=v[mid+j];
}
if(num!=-1) V2[num].push_back(sum);
}
for(int i=0;i<=N-mid;i++) sort(V2[i].begin(),V2[i].end());
}
struct node{int i,j,x;};
inline bool operator <(const node x,const node y){return x.x<y.x;}
priority_queue<node>q;
int match[maxn];
int main()
{
c[0][0]=1;
for(int i=1;i<maxn;i++)
{
c[i][0]=1;
for(int j=1;j<maxn;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%Mod;
}
int tcase; scanf("%d",&tcase);
while(tcase--)
{
scanf("%d%d",&n,&val);
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
N=n;
m=0; for(int i=1;i<=n;i++) if(v[i]!=-1) m++;
n=N-m;
for(int i=0;i<=m;i++) cal(i);
for(int i=m;i>=0;i--)
{
f[i]=g[i];
for(int j=i+1;j<=m;j++)
dec(f[i],(ll)f[j]*c[m-i][j-i]%Mod);
}
search();
int mid=N>>1; int re=0;
for(int i=0;i<=mid;i++)
{
memset(match,0,sizeof match);
while(!q.empty()) q.pop();
for(int j=0;j<=N-mid;j++) if(V2[j].size())
q.push((node){j,V2[j].size()-1,V2[j][V2[j].size()-1]}),match[j]=V2[j].size();
for(int j=0;j<V1[i].size();j++)
{
int cc=V1[i][j];
if(q.empty()) break;
while(!q.empty())
{
node now=q.top(); if(cc+now.x<=val) break;
q.pop();
for(;now.j>=0&&V2[now.i][now.j]+cc>val;now.j--) match[now.i]--;
if(now.j<0) continue;
now.x=V2[now.i][now.j];
q.push(now);
}
for(int l=0;l<=N-mid;l++) if(match[l])
add(re,(ll)match[l]*f[m-(i+l)]%Mod);
}
}
printf("%d\n",re);
}
return 0;
}