[JZOJ5130][51nod1446][SDOI省队集训2017]苹果树

题目描述

有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+1Cjixif[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);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值