51nod 1446

11 篇文章 0 订阅
9 篇文章 0 订阅

一个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个里面有 Cjimi 种情况,每种对应一个f[j],有 f[i]=g[i]mj=i+1Cjimif[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值