loj#6072 苹果树(折半搜索,矩阵树定理,容斥)

loj#6072 苹果树(折半搜索,矩阵树定理,容斥)

loj

题解时间

$ n \le 40 $ 。

无比精确的数字。

很明显只要一个方案不超过 $ limits $ ,之后的计算就跟选哪个没关系了。

折半搜索排序来统计有i个果子是有用的情况下的方案数。

然后矩阵树求生成树个数,容斥乱搞。

#include<bits/stdc++.h>
using namespace std;
template<typename TP>inline void read(TP &tar)
{
    TP ret=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();}
    tar=ret*f;
}
namespace RKK
{
const int N=50,mo=1000000007;
void doadd(int &a,int b){if((a+=b)>=mo)a-=mo;}
int add(int a,int b){return (a+=b)>=mo?a-mo:a;}
int fpow(int a,int p){int ret=1;while(p){if(p&1)ret=1ll*ret*a%mo;a=1ll*a*a%mo,p>>=1;}return ret;}
struct pat{int x,y;bool operator < (const pat &p)const{return x<p.x;}};
int n,halfn,lim,val[N],msn;
pat l1[1145141];int tp1;
pat l2[1145141];int tp2;
void dfs1(int x=1,int sum=0,int cnt=0)
{
    if(sum>lim) return;if(x>halfn){l1[++tp1]=(pat){sum,cnt};return;}
    dfs1(x+1,sum,cnt);if(~val[x]) dfs1(x+1,sum+val[x],cnt+1);
}
void dfs2(int x=halfn+1,int sum=0,int cnt=0)
{
    if(sum>lim) return;if(x>n){l2[++tp2]=(pat){sum,cnt};return;}
    dfs2(x+1,sum,cnt);if(~val[x]) dfs2(x+1,sum+val[x],cnt+1);
}
int c[N][N];void init(){for(int i=0;i<=40;i++){c[i][0]=1;for(int j=1;j<=i;j++)c[i][j]=add(c[i-1][j-1],c[i-1][j]);}}
int ma[N][N];
int calc(int sn)
{
    memset(ma,0,sizeof(ma));
    for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)
    {
        if((i<=sn&&(j<=sn||j>msn))||i>msn||j>msn)
            ma[i][i]++,ma[j][j]++,doadd(ma[i][j],mo-1),doadd(ma[j][i],mo-1);
    }
    int b=n-1;int f=1;
    for(int l=1;l<=b;l++)
    {
        int g=l;for(;g<=b&&!ma[g][l];g++);if(g>b) return 0;
        if(g!=l){for(int j=l;j<=b;j++) swap(ma[l][j],ma[g][j]);f=-f;}
        for(g=l+1;g<=b;g++)
        {
            int k=1ll*ma[g][l]*fpow(ma[l][l],mo-2)%mo;
            for(int j=l;j<=b;j++) doadd(ma[g][j],mo-1ll*ma[l][j]*k%mo);
        }
    }
    if(f==-1) f=mo-1;
    for(int i=1;i<=b;i++) f=1ll*f*ma[i][i]%mo;
    return f;
}
int cnt[N],sum[N];
int cnttmp[N];
int main()
{
    read(n),read(lim),halfn=n+1>>1;
    for(int i=1;i<=n;i++) read(val[i]),msn+=(val[i]!=-1);
    dfs1(),dfs2();init();
    sort(l1+1,l1+tp1+1),sort(l2+1,l2+tp2+1);
    for(int i1=tp1,i2=1;i1;i1--)
    {
        for(;i2<=tp2&&l1[i1].x+l2[i2].x<=lim;cnttmp[l2[i2].y]++,i2++);
        for(int j=0;j<=n;j++) doadd(cnt[l1[i1].y+j],cnttmp[j]);
    }
    for(int i=0;i<=msn;i++) sum[i]=calc(i);
    for(int i=1;i<=msn;i++)for(int j=0;j<i;j++)
        doadd(sum[i],mo-1ll*c[i][j]*sum[j]%mo);
    int ans=0;
    for(int i=0;i<=msn;i++) doadd(ans,1ll*cnt[i]*sum[i]%mo);
    printf("%d\n",ans);
    return 0;
}
}
int main(){return RKK::main();}

原文地址:https://www.cnblogs.com/rikurika/p/12245933.html

时间: 2024-08-27 23:49:02

loj#6072 苹果树(折半搜索,矩阵树定理,容斥)的相关文章

矩阵树定理速证

凯莱公式: spanning_trees_num( G ) = spanning_trees_num( G - e ) + spanning_trees_num( G · e ) 矩阵树定理: G 相应的拉普拉斯矩阵(度矩阵 - 邻接矩阵)L( G )   删除随意一行一列得到的行列式的值det( L*( G ) ) 即生成树的个数,即spanning_trees_num( G ) = det( L*( G ) ) 证: 归纳如果 spanning_trees_num( G - e ) = de

[spoj104][Highways] (生成树计数+矩阵树定理+高斯消元)

In some countries building highways takes a lot of time... Maybe that's because there are many possiblities to construct a network of highways and engineers can't make up their minds which one to choose. Suppose we have a list of cities that can be c

CSU 1805 Three Capitals(矩阵树定理+Best定理)

http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1805 题意: A和B之间有a条边,A和G之间有b条边,B和G之间有c条边.现在从A点出发走遍所有的边,然后再回到A点,问一共有多少种方法. 思路: 16年湖南省赛题目,这道题目是求欧拉回路的个数,和生成树的计数有一定的联系. 首先给出神奇的Best定理,这是什么鬼定理,反正查不到什么有关该定理的文章... $ec(G)=t_s(G)\cdot deg(s)! \cdot \prod_{v\i

【BZOJ4031】[HEOI2015]小Z的房间 矩阵树定理

[BZOJ4031][HEOI2015]小Z的房间 Description 你突然有了一个大房子,房子里面有一些房间.事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子.在一开始的时候,相邻的格子之间都有墙隔着. 你想要打通一些相邻房间的墙,使得所有房间能够互相到达.在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙).同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路.现在,你希望统计一共有多少种可行的方案.

【bzoj2467】[中山市选2010]生成树 矩阵树定理

题目描述 有一种图形叫做五角形圈.一个五角形圈的中心有1个由n个顶点和n条边组成的圈.在中心的这个n边圈的每一条边同时也是某一个五角形的一条边,一共有n个不同的五角形.这些五角形只在五角形圈的中心的圈上有公共的顶点.如图0所示是一个4-五角形圈. 现在给定一个n五角形圈,你的任务就是求出n五角形圈的不同生成树的数目.还记得什么是图的生成树吗?一个图的生成树是保留原图的所有顶点以及顶点的数目减去一这么多条边,从而生成的一棵树. 注意:在给定的n五角形圈中所有顶点均视为不同的顶点. 输入 输入包含多

【bzoj4894】天赋 矩阵树定理

题目描述 小明有许多潜在的天赋,他希望学习这些天赋来变得更强.正如许多游戏中一样,小明也有n种潜在的天赋,但有一些天赋必须是要有前置天赋才能够学习得到的.也就是说,有一些天赋必须是要在学习了另一个天赋的条件下才能学习的.比如,要想学会"开炮",必须先学会"开枪".一项天赋可能有多个前置天赋,但只需习得其中一个就可以学习这一项天赋.上帝不想为难小明,于是小明天生就已经习得了1号天赋-----"打架".于是小明想知道学习完这n种天赋的方案数,答案对1

【整合】矩阵树定理模板

矩阵树定理求生成树计数模板. 原题是SPOJhighways 代码又长又丑- #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define MAXN 20 #define eps 1e-9 using namespace std; int A[MAXN][MAXN],D[MAXN][MAXN];//A是邻接矩阵D是

走进矩阵树定理--「CodePlus 2017 12 月赛」白金元首与独舞

n,m<=200,n*m的方阵,有ULRD表示在这个格子时下一步要走到哪里,有一些待决策的格子用.表示,可以填ULRD任意一个,问有多少种填法使得从每个格子出发都能走出这个方阵,答案取模.保证未确定的格子<=300. ...一脸懵逼地写了原本30实际20的暴力然后跪着啃了下论文 然而什么都没啃懂不如结论记下来: 首先矩阵行列式的定义:一个n*n的矩阵,行列式值为$\sum_{b是n的一个排列} \ \ \ \ \ ( (-1)^{b的逆序对数} \ \ \ \ \ * \prod_{i=1}^

【Luogu】P3317重建(高斯消元+矩阵树定理)

题目链接 因为这个专门跑去学了矩阵树定理和高斯消元qwq 不过不是很懂.所以这里只放题解 玫葵之蝶的题解 某未知dalao的矩阵树定理 代码 #include<cstdio> #include<cstdlib> #include<cctype> #include<algorithm> #include<cstring> #include<cmath> #define eps 1e-8 #define maxn 100 using na