51nod1446 Kirchhoff矩阵+Gauss消元+容斥+折半DFS

思路:

//By SiriusRen
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int mod=1000000007;
int cases,n,maxval,a[44][44],C[44][44],f[44],val[44],X,g[44];
int top2,top,bgn,half,cnts2[22],allnum[44],Ans,T;
struct Node{int wei,num;Node(){}Node(int x,int y){wei=x,num=y;}}s[5+(1<<20)],s2[5+(1<<20)];
bool operator<(Node a,Node b){return a.wei<b.wei;}
int pow(ll x,int y){
    ll res=1;
    while(y){
        if(y&1)res=res*x%mod;
        x=x*x%mod,y>>=1;
    }return (int)res;
}
int Gauss(int n){
    int f=1;
    for(int i=1;i<=n;i++){
        int j=i;while(!a[i][j]&&j<=n)j++;
        if(j==n+1)return 0;
        if(j!=i){
            for(int k=1;k<=n;k++)swap(a[i][k],a[j][k]);
            f*=-1;
        }
        int t=pow(a[i][i],mod-2);
        for(int j=i+1;j<=n;j++){
            int ww=1ll*a[j][i]*t%mod;
            for(int k=i;k<=n;k++)a[j][k]=(a[j][k]-1ll*ww*a[i][k]%mod+mod)%mod;
        }
    }
    ll ans=1;
    for(int i=1;i<=n;i++)ans=ans*a[i][i]%mod;
    if(f==-1)ans=(mod-ans)%mod;
    return ans;
}
void Kirchhoff(int x){
    memset(a,0,sizeof(a));
    for(int i=1;i<=n;i++)
        if(i<=x)for(int j=max(i+1,X+1);j<=n;j++)a[i][j]--,a[j][i]--,a[i][i]++,a[j][j]++;
        else for(int j=i+1;j<=n;j++)a[i][j]--,a[j][i]--,a[i][i]++,a[j][j]++;
}
bool cmp(int x,int y){return x>y;}
void dfs(int x,int wei,int deep){
    !T?s[top++]=Node(wei,deep):s2[top2++]=Node(wei,deep);
    for(int i=x;i>bgn;i--)if(wei+val[i]<=maxval)dfs(i-1,wei+val[i],deep+1);
}
signed main(){
    for(int i=0;i<=40;i++){
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
    scanf("%d",&cases);
    while(cases--){
        memset(allnum,0,sizeof(allnum));
        memset(cnts2,0,sizeof(cnts2));
        top=top2=X=bgn=Ans=T=0;
        scanf("%d%d",&n,&maxval);
        for(int i=1;i<=n;i++){
            scanf("%d",&val[i]);
            if(~val[i])X++;
        }half=(X+1)/2;
        for(int i=0;i<=X;i++)Kirchhoff(i),f[i]=g[i]=Gauss(n-1);
        for(int i=X;~i;i--)
            for(int j=i+1;j<=X;j++)f[i]=((f[i]-1ll*C[X-i][j-i]*f[j])%mod+mod)%mod;
        sort(val+1,val+1+n,cmp),random_shuffle(val+1,val+1+X);
        dfs(half,0,0),sort(s,s+top);
        T=1,bgn=half,dfs(X,0,0),sort(s2,s2+top2);
        for(int i=0;i<top2;i++)cnts2[s2[i].num]++;
        for(int i=0,j=top2-1;i<top;i++){
            while(s[i].wei+s2[j].wei>maxval)cnts2[s2[j].num]--,j--;
            for(int k=0;k<=X-half;k++)(allnum[k+s[i].num]+=cnts2[k])%=mod;
        }
        for(int i=0;i<=X;i++)Ans=(Ans+1ll*f[X-i]*allnum[i])%mod;
        printf("%d\n",(Ans+mod)%mod);
    }
}
时间: 2024-10-22 19:45:44

51nod1446 Kirchhoff矩阵+Gauss消元+容斥+折半DFS的相关文章

Gauss 消元(模板)

1 /* 2 title:Gauss消元整数解/小数解整数矩阵模板 3 author:lhk 4 time: 2016.9.11 5 没学vim的菜鸡自己手打了 6 */ 7 #include<cstdio> 8 #include<iostream> 9 #include<cstring> 10 #include<algorithm> 11 #include<cmath> 12 #define clr(x) memset(x,0,sizeof(x

求一个n元一次方程的解,Gauss消元

const Matrix=require('./Matrix.js') /*Gauss 消元 传入一个矩阵,传出结果 */ function Gauss(matrix){ let l=[];//是否为自由元 let ans=[];//存储解 const n=matrix.Column-1;//解的个数 const EPS=0.00001; let res=0,r=0; for(let i=0;i<matrix.Column;i++){ for(let j=r;j<matrix.Row;j++)

hdu 5755(GAuss 消元)

Gambler Bo Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1152    Accepted Submission(s): 471Special Judge Problem Description Gambler Bo is very proficient in a matrix game. You have a N×M m

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)

bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥)

bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥) bzoj Luogu 题解时间 看一看数据范围,求生成树个数毫无疑问直接上矩阵树定理. 但是要求每条边都属于不同公司就很难直接实现. 按套路上容斥: 如果直接将几个公司的修路列表加进矩阵里的话,求出来的是"只使用"这些边的生成树个数. 很明显上容斥之后就会直接变成"只使用"且"每个都被使用"的个数. 正好符合题目要求的生成树的n-1条边分属于n-1个公

POJ1830开关问题——gauss消元

题目链接 分析: 第一个高斯消元题目,操作是异或.奇偶能够用0.1来表示,也就表示成bool类型的方程,操作是异或.和加法没有差别 题目中有两个未知量:每一个开关被按下的次数(0.1).每一个开关的转换次数. 题目仅仅和操作次数的奇偶有关,所以用0.1表示之后,对于每一个开关的转换次数就已经知道了.所以仅仅有一个未知量.能够线性表示.练习使用模板 const int maxn = 40; int a[maxn][maxn]; int gauss(int N, int M) { int r, c,

【BZOJ3503】【Cqoi2014】和谐矩阵 高斯消元,解异或方程组

#include <stdio.h> int main() { puts("转载请注明出处"); puts("地址:blog.csdn.net/vmurder/article/details/43699831"); } 题解: 随便搞搞就好. 自由元全当成1就好了么~~~ 不会异或方程组的移步这里[POJ1222]EXTENDED LIGHTS OUT 高斯消元.解异或方程组 代码: #include <cstdio> #include &l

【Luogu】P3389高斯消元模板(矩阵高斯消元)

题目链接 高斯消元其实是个大模拟qwq 所以就着代码食用 首先我们读入 for(int i=1;i<=n;++i) for(int j=1;j<=n+1;++j) scanf("%lf",&s[i][j]); 读入肯定没什么问题(不过我在这卡了一分多钟) 然后我们要进行消元操作 所谓消元操作其实就是对于输入的矩阵 比如说 9 3 2 2 1 4 7 3 1 3 4 5 进行一番乱搞,使得第当前枚举的(比如说枚举第i行第i列)s[i][j]系数变成1. 实际上就是整行

bzoj1002 轮状病毒 暴力打标找规律/基尔霍夫矩阵+高斯消元

基本思路: 1.先观察规律,写写画画未果 2.写程序暴力打表找规律,找出规律 1-15的答案:1    5    16    45    121 320 841     2205   5776 15125 39601  103680  271441    710645      1860496 第1.3.5.7...[奇数位]位是平方数 : 1*1  4*4  11*11   29*29   76*76   199*199  521*521... 第2.4.6.8...[偶数位]位除以5后也是平