Loading

51nod-1446-价值限制树

51nod-1446-价值限制树

真·一调一下午。

题意:给\(n\)个点,每个点有权值\(a_i\),若\(a_i=-1\)称这个点\(not good\),否则是\(good\)的。形成任意一棵生成树后若\(good\)的点相邻的点有\(good\)点,则称该点是\(great\)的。生成树的价值是所有\(great\)的点的权值和。给定\(maxval\)值,求所有生成树价值不超过\(maxval\)的生成树个数。

题解:显然需要建图跑矩阵树定理。可以发现一个性质,就是在\(good\)的点中选出几个点作为\(great\)点之后,形成的生成树方案是一定的。所以设\(f[i]\)为选出了\(i\)个点作为\(great\)点的生成树方案,发现\(f[i]\)并不能建图后直接求。不妨设\(h[i]\)表示最多有\(i\)个点是\(great\)点的生成树种类,则只需将\(great\)点相互连边,不是\(good\)的点向其他点连边,然后跑矩阵树定理即可求\(h[i]\)

由于\(h[i]\)的状况是有i个点可以互相连通形成\(great\)点的生成树方案数,实际上可能若这\(i\)个点中某个点成为叶子节点,且父节点是\(not good\)的节点,那么会不满\(i\)个,此时会有\(j\)个节点被选出,这样的情况总和是\(\sum^{i-1}_{j=0}C_{i}^{j}f[j]\),于是由容斥定理:

\[f[i]=h[i]-\sum^{i-1}_{j=0}C_{i}^{j}f[j] \]

可以计算出\(f[i]\)

在每种\(f[i]\)的情况下,需要满足价值不超过\(maxval\)的限制条件。也就是我们在所有\(good\)的点中选i个使其权值和不超过\(maxval\)

设其种类数为\(g[i]\),则可以使用折半搜索\((n≤40)\)计算出结果。折半搜索的模版是:P4799世界冰球锦标赛

于是我们要求的总答案即为\(\sum_{i=0}^{gd}f[i]g[i]\)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mod (ll)(1e9+7)
const int maxn=3e5+10;
int t,n,maxval,gd,ngd;
ll yuan[50],f[50],g[50],h[50];
ll gdb[50],bd[50];
bool good[50];
int qian[50][maxn],hou[50][maxn],cntq[50],cnth[50];//vector
void pre(){
    memset(yuan,0,sizeof(yuan));
    memset(f,0,sizeof(f));memset(g,0,sizeof(g));
    memset(h,0,sizeof(h));
    memset(gdb,0,sizeof(gdb));
    memset(bd,0,sizeof(bd));
    memset(good,0,sizeof(good));
    gd=ngd=0;
    memset(qian,0,sizeof(qian));memset(hou,0,sizeof(hou));
    memset(cntq,0,sizeof(cntq));
    memset(cnth,0,sizeof(cnth));
}
struct matrix{
    ll ma[50][50];
    void pre(){memset(ma,0,sizeof(ma));}
}du;
ll gauss(){
    n--;
    ll ans=1;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            while(du.ma[j][i]){
                ll tmp=du.ma[i][i]/du.ma[j][i];
                for(int k=i;k<=n;k++) du.ma[i][k]=((du.ma[i][k]-tmp*du.ma[j][k]%mod)%mod+mod)%mod,swap(du.ma[i][k],du.ma[j][k]);
                ans=-ans;
            }
        }
        ans=(ans*du.ma[i][i]%mod+mod)%mod;
    }
    n++;
    return (ans+mod)%mod;
}
void solh(){
    for(int i=0;i<=gd;i++){
        du.pre();
        int now1=i,now2=gd-i;
        for(int j=1;j<=now1;j++){
            for(int k=j+1;k<=now1;k++){
                du.ma[j][k]=-1,du.ma[k][j]=-1;
                du.ma[j][j]++,du.ma[k][k]++;
            }
        }
        for(int j=gd+1;j<=n;j++){
            for(int k=1;k<j;k++){
                du.ma[j][j]++,du.ma[k][k]++;
                du.ma[j][k]=-1,du.ma[k][j]=-1;
            }
        }
        h[i]=gauss();
    }
}
void dfsq(int qian1,int hou1,int val,int ge){
    if(val>maxval) return;
    if(qian1==hou1){
        if(val+yuan[gdb[qian1]]<=maxval) qian[ge+1][++cntq[ge+1]]=(val+yuan[gdb[qian1]]);
        qian[ge][++cntq[ge]]=val;
        return;
    }
    dfsq(qian1+1,hou1,(val+yuan[gdb[qian1]]),ge+1);
    dfsq(qian1+1,hou1,val,ge);
}
void dfsh(int qian1,int hou1,int val,int ge){
    if(val>maxval) return;
    if(qian1==hou1){
        if(val+yuan[gdb[qian1]]<=maxval) hou[ge+1][++cnth[ge+1]]=(val+yuan[gdb[qian1]]);
        hou[ge][++cnth[ge]]=val;
        return;
    }
    dfsh(qian1+1,hou1,(val+yuan[gdb[qian1]]),ge+1);
    dfsh(qian1+1,hou1,val,ge);
}
void solg(){
    ll mid=gd/2;
    g[0]=1;
    if(gd==1){
        if(yuan[gdb[1]]<=maxval) g[1]=1;
        return;
    }
    else if(gd==0) return;
    g[0]=0;
    dfsq(1,mid,0,0);dfsh(mid+1,gd,0,0);
    for(int i=1;i<=gd;i++){
        sort(qian[i]+1,qian[i]+cntq[i]+1);
        sort(hou[i]+1,hou[i]+cnth[i]+1);
    }
    for(int i=0;i<=gd;i++){
        for(int j=0;j<=i;j++){
            int nowq=j,nowh=i-j;
            for(int p=1;p<=cntq[nowq];p++){
                ll sheng=maxval-qian[nowq][p];
                ll biao=upper_bound(hou[nowh]+1,hou[nowh]+cnth[nowh]+1,sheng)-hou[nowh]-1;
                g[i]=(g[i]+biao)%mod;
            }
        }
    }
}
int cm[50][50];
signed main(){
    cin>>t;
    cm[1][1]=1;
    for(int i=0;i<=45;i++) cm[i][0]=1;
    for(int i=2;i<=45;i++){
        for(int j=1;j<=i;j++){
            cm[i][j]=(cm[i-1][j]+cm[i-1][j-1])%mod;
        }
    }
    while(t--){
        cin>>n>>maxval;
        pre();
        for(int i=1;i<=n;i++){
            cin>>yuan[i];
            if(yuan[i]>=0) gdb[++gd]=i,good[i]=1;
            else bd[++ngd]=i,good[i]=0;
        }
        solh();
        for(int i=0;i<=gd;i++){
            f[i]=h[i];
            for(int j=0;j<i;j++) f[i]=((f[i]-(ll)cm[i][j]*f[j]%mod)%mod+mod)%mod;
        }
        solg();
        ll ans=0;
        for(int i=0;i<=gd;i++) ans=(ans+(ll)f[i]*g[i]%mod)%mod;
        cout<<ans<<endl;
    }
    return 0;
}
posted @ 2021-07-18 21:30  14long  阅读(16)  评论(0)    收藏  举报