[51Nod1446] 限制价值树 (容斥+MT定理+折半搜索)
Description
有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后的结果。
说明:两棵树是不同的,指两棵树的边集不同,注意这里的边都是无向边。
Input
多组测试数据,第一行一个整数T,表示测试数据数量,1<=T<=5
每组测试数据有相同的结构构成:
每组数据第一行两个整数N与maxVal,满足1<=N<=40,0<=maxVal<=1,000,000,000。
第二行有N个整数,即val[0] ~ val[N-1]的数值,满足-1<=val[i]<=25,000,000。
Output
每组数据一行输出,即限制价格树的个数。
Sample Input
3
4 3
1 2 -1 3
4 5
1 2 -1 3
5 6
-1 -1 2 5 5
Sample Output
3
7
20
Solution
f[i]表示至少有i个good点不great的生成树个数(这i个good点只选一种情况)
g[i]表示恰有i个good点不great的生成树个数(这i个good点只选一种情况)
以上两个数组可以只用一个数组qwq
h[i]表示这tot个good点中使i个为great并且权值不超过maxval的方案数
最终答案就是\(\sum_{i=1}^{tot}g[i]*h[tot-i]\)
f[i]直接由矩阵树定理求出,g[i]显然可以由f[i]递推得到
h[i]直接折半搜索
PS:注意不要爆空间
Code
//By Menteur_Hxy
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define F(i,a,b) for(register int i=(a);i<=(b);i++)
#define R(i,a,b) for(register int i=(b);i>=(a);i--)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
inline LL read() {
LL x=0,f=1; char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
return x*f;
}
const int N=41,MOD=1e9+7,MAX=1048580;
int n,m,tot,cnt,ans;
int val[N],g[N],h[N],mat[N][N],fac[N],inv[N],Cnt[MAX][21];//注意空间大小
PII da[MAX];
LL qpow(LL a,LL b) {
LL t=1;
while(b) {
if(b&1) t=t*a%MOD;
a=a*a%MOD; b>>=1;
}
return t;
}
void init() {
fac[0]=1; F(i,1,40) fac[i]=(LL)fac[i-1]*i%MOD;
inv[40]=qpow(fac[40],MOD-2);
R(i,0,40-1) inv[i]=(LL)inv[i+1]*(i+1)%MOD;
}
LL C(int m,int n) {return (LL)fac[m]*inv[m-n]%MOD*inv[n]%MOD;}
void print() {
F(i,1,n) {
F(j,1,n) cout<<mat[i][j]<<" ";
cout<<endl;
}cout<<endl;
}
LL getf(int x) {
LL res=1,fla=1;
memset(mat,0,sizeof(mat));
F(i,1,x) F(j,tot+1,n) mat[i][i]++,mat[j][j]++,mat[i][j]--,mat[j][i]--;
F(i,x+1,n) F(j,i+1,n) mat[i][i]++,mat[j][j]++,mat[i][j]--,mat[j][i]--;//注意连边
// print();
// cout<<x<<endl;
F(i,1,n-1) {//注意去掉一行一列!!
int mx=i; F(j,i,n-1) if(mat[j][i]!=0) {mx=j;break;}
if(mx!=i) {F(j,i,n-1) swap(mat[i][j],mat[mx][j]);fla=-fla;}
if(!mat[mx][i]) return 0;
res=(LL)res*mat[i][i]%MOD;
LL Inv=qpow(mat[i][i],MOD-2);
F(j,i,n-1) mat[i][j]=(LL)mat[i][j]*Inv%MOD;
F(j,i+1,n-1) {
LL tmp=mat[j][i]; mat[j][i]=0;
F(k,i+1,n-1) mat[j][k]=(mat[j][k]-(LL)tmp*mat[i][k]%MOD+MOD)%MOD;
}
// print();
}
return res*fla;
}
void dfs1(int l,int r,int num,int sum) {
if(l>r) {da[++cnt]=PII(sum,num);return ;}
dfs1(l+1,r,num,sum);
if(sum+val[l]<=m) dfs1(l+1,r,num+1,sum+val[l]);
}
void dfs2(int l,int r,int num,int sum) {
if(l>r) {
int p=upper_bound(da+1,da+1+cnt,PII(m-sum,n+1))-da-1;//!!!
F(i,0,tot/2) h[i+num]=(h[i+num]+Cnt[p][i])%MOD;
return ;
}
dfs2(l+1,r,num,sum);
if(sum+val[l]<=m) dfs2(l+1,r,num+1,sum+val[l]);
}
bool cmp(int x,int y) {return x>y;}
void solve() {
n=read(),m=read();tot=cnt=ans=0;
F(i,1,n) val[i]=read(),tot+=(val[i]!=-1);
sort(val+1,val+1+n,cmp);
F(i,0,tot) g[i]=getf(i);
// F(i,0,tot) cout<<g[i]<<" ";cout<<endl;
R(i,0,tot) F(j,i+1,tot) g[i]=(g[i]-(LL)C(tot-i,j-i)*g[j]%MOD+MOD)%MOD;
dfs1(1,tot/2,0,0);
sort(da+1,da+1+cnt);
F(i,1,cnt) {
F(j,0,tot/2) Cnt[i][j]=Cnt[i-1][j];
Cnt[i][da[i].second]++;
}
memset(h,0,sizeof(h));//初始化!!
dfs2(tot/2+1,tot,0,0);
F(i,0,tot) ans=(ans+(LL)g[i]*h[tot-i]%MOD)%MOD;
// F(i,1,tot) cout<<val[i]<<" ";cout<<endl;cout<<endl;
// F(i,1,cnt) cout<<da[i].first<<" "<<da[i].second<<endl;cout<<endl;
// F(i,1,cnt) F(j,1,10) if(Cnt[i][j]) cout<<
// F(i,0,tot) cout<<g[i]<<" "<<h[i]<<endl;
printf("%lld\n",ans);
}
int main() {
init();
int T=read();
while(T--) solve();
return 0;
}
[51Nod1446] 限制价值树 (容斥+MT定理+折半搜索)的更多相关文章
- 洛谷P5206 [WC2019]数树 [容斥,DP,生成函数,NTT]
传送门 Orz神仙题,让我长了许多见识. 长式子警告 思路 y=1 由于y=1时会导致后面一些式子未定义,先抓出来. printf("%lld",opt==0?1:(opt==1? ...
- Codeforces.348D.Turtles(容斥 LGV定理 DP)
题目链接 \(Description\) 给定\(n*m\)的网格,有些格子不能走.求有多少种从\((1,1)\)走到\((n,m)\)的两条不相交路径. \(n,m\leq 3000\). \(So ...
- NOIP2019模拟2019.9.20】膜拜大会(外向树容斥,分类讨论)
传送门. 题解: 我果然是不擅长分类讨论,心态被搞崩了. 注意到\(m<=n-2\),意味着除了1以外的位置不可能被加到a[1]两遍. 先考虑个大概: 考虑若存在\(x,x-1,-,2\)(有序 ...
- 51nod1446 限制价值树
有N个点(N<=40)标记为0,1,2,...N-1,每个点i有个价值val[i],如果val[i]=-1那么这个点被定义为bad,否则如果val[i] >=0那么这个点为定义为good. ...
- HDU - 4059: The Boss on Mars (容斥 拉格朗日 小小的优化搜索)
pro: T次询问,每次给出N(N<1e8),求所有Σi^4 (i<=N,且gcd(i,N)==1) ; sol: 因为N比较小,我们可以求出素因子,然后容斥. 主要问题就是求1到P的 ...
- [Codeforces235D]Graph Game——概率与期望+基环树+容斥
题目链接: Codeforces235D 题目大意:给出一棵基环树,并给出如下点分治过程,求点数总遍历次数的期望. 点分治过程: 1.遍历当前联通块内所有点 2.随机选择联通块内一个点删除掉 3.对新 ...
- hdu6059 Kanade's trio 字典树+容斥
转自:http://blog.csdn.net/dormousenone/article/details/76570172 /** 题目:hdu6059 Kanade's trio 链接:http:/ ...
- [BZOJ 1042] [HAOI2008] 硬币购物 【DP + 容斥】
题目链接:BZOJ - 1042 题目分析 首先 Orz Hzwer ,代码题解都是看的他的 blog. 这道题首先使用DP预处理,先求出,在不考虑每种硬币个数的限制的情况下,每个钱数有多少种拼凑方案 ...
- bzoj 4596 [Shoi2016]黑暗前的幻想乡 矩阵树定理+容斥
4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 559 Solved: 325[Submit][Sta ...
随机推荐
- 设计模式实例(Lua)笔记之五(Bridge模式)
1.描写叙述 今天我要说说我自己,梦想中的我自己,我身价过亿,有两个大公司,一个是房地产公司,一个是服装制造业,这两个公司都非常赚钱,天天帮我在累加財富,事实上是什么公司我倒是不关心,我关心的是是不是 ...
- LeetCode 234 Palindrome Linked List(回文链表)(*)(?)
翻译 给定一个单链表,确定它是否是回文的. 跟进: 你能够在O(n)时间和O(1)空间下完毕它吗? 原文 Given a singly linked list, determine if it is ...
- STL_算法_逆转(reverse,reverse_copy)
C++ Primer 学习中.. . 简单记录下我的学习过程 (代码为主) //全部容器适用 reverse(b,e) //逆转区间数据 reverse_copy(b,e,b2) /** ...
- 摄像头ov2685中关于sensor id 设置的相关的寄存器地址【转】
本文转载自:http://blog.csdn.net/morixinguan/article/details/51220992 OV2685 : CHIP_ID address : 0x300A ...
- Linux下RTC时间的读写分析【转】
本文转载自:http://blog.csdn.net/little_walt/article/details/52880840 Linux系统下包含两个时间:系统时间和RTC时间. 系统时间:是由主芯 ...
- C# Socket 您的主机中的软件中止了一个已建立的连接 An established connection was aborted by the software in your host machine
http://tieba.baidu.com/p/3223234493 问题: 服务端在接收客户端数据的时候,抛了个异常出来:System.Net.Sockets.SocketException: 您 ...
- Anaconda/kickstart
http://fedoraproject.org/wiki/Anaconda/Kickstart/zh-cn
- compare正序与逆序
//list:在数据查询出来的Record集合 //juli:是需要比较的字段 //实现一个Comparator接口 //后面减去前面是正序 前面减去后面是倒叙 //我这里做的一个距离排序 R ...
- 3Ddungeon-------三维搜索-----偷个懒 把 亡命逃窜 的代码修改了一下 拿来用了
题 很简单 就是给一个 三维的迷宫然后 开你起始地点 S 问你能不能到达 出口 E 能的话 需要多长时间 ? #include<stdio.h> #include<string ...
- A - HQ9+
Problem description HQ9+ is a joke programming language which has only four one-character instructio ...