「2017 山东一轮集训 Day5」苹果树

\(n\leq 40\)

折半搜索+矩阵树定理。

没有想到折半搜索。

首先我们先枚举\(k\)个好点,我们让它们一定没有用的。要满足这个条件就要使它只能和坏点相连。其他的点没有要求。这样算出来了至少\(k\)个点没有用的生成树个数,我们要得到恰好\(k\)个点的生成树个数就简单容斥一下就好了。

然后我们要得到有\(k\)个点没有用的情况下的点集的方案数。看到\(40\)这个范围我们容易想到折半搜索。

然后就没了。

但是我没写容斥,写的枚举集合划分(被吊打

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 45 using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} const ll mod=1e9+7;
int n;
ll lim;
int a[N];
ll c[N][N];
ll fac[N],ifac[N],g[N];
ll w[N][N];
ll sum;
ll ksm(ll t,ll x) {
ll ans=1;
for(;x;x>>=1,t=t*t%mod)
if(x&1) ans=ans*t%mod;
return ans;
} ll f[N];
ll ned;
int good[N];
ll bad;
int num[N];
ll ans;
ll Gauss(ll a[][45],int n) {
ll ans=1;
for(int i=2;i<=n;i++) {
for(int j=i+1;j<=n;j++) {
if(a[j][i]) {
ans=ans*(mod-1)%mod;
swap(a[i],a[j]);
break;
}
}
if(!a[i][i]) return 0;
ans=ans*a[i][i]%mod;
for(int j=i+1;j<=n;j++) {
if(!a[j][i]) continue ;
ll tem=ksm(a[i][i],mod-2)*a[j][i]%mod;
for(int k=i;k<=n;k++) a[j][k]=(a[j][k]-tem*a[i][k]%mod+mod)%mod;
}
}
return ans;
} vector<int>st;
ll cal() {
if(!f[num[1]]) return 0;
int tot=bad;
st.clear();
for(int i=1;i<=n;i++) for(int j=1;j<=num[i];j++) st.push_back(i);
for(int i=1;i<=n;i++) tot+=num[i];
memset(w,0,sizeof(w)); for(int i=0;i<st.size();i++) {
w[i+1][i+1]=st[i]*bad;
for(int j=st.size()+1;j<=tot;j++) {
w[i+1][j]=mod-st[i];
}
}
for(int i=st.size()+1;i<=tot;i++) {
w[i][i]=n-1;
for(int j=st.size()+1;j<=tot;j++)
if(i!=j) w[i][j]=mod-1;
for(int j=0;j<st.size();j++)
w[i][j+1]=mod-st[j];
} ll ans=f[num[1]];
ans=ans*fac[n-bad-num[1]]%mod%mod;
for(int i=2;i<=n;i++) {
ans=ans*ksm(ifac[i],num[i])%mod;
ans=ans*ksm(g[i],num[i])%mod;
ans=ans*ifac[num[i]]%mod;
} ll tem=Gauss(w,tot);
return ans*tem%mod;
} int mid;
void dfs(int v,int lim,ll now,int cnt,vector<int>*a) {
if(v>lim) return ;
dfs(v+1,lim,now,cnt,a);
a[cnt+1].push_back(now+good[v]);
dfs(v+1,lim,now+good[v],cnt+1,a);
} void meet_in_the_middle() {
mid=good[0]>>1;
vector<int>a[N],b[N];
dfs(1,mid,0,0,a);
dfs(mid+1,good[0],0,0,b);
for(int i=1;i<=mid;i++) sort(a[i].begin(),a[i].end());
for(int i=1;i<=mid;i++)
for(int j=0;j<a[i].size();j++)
if(a[i][j]>=ned) f[i]++;
for(int i=1;i<=good[0]-mid;i++) {
for(int j=0;j<b[i].size();j++) {
if(b[i][j]>=ned) f[i]++;
for(int k=1;k<=mid;k++) {
(f[i+k]+=a[k].end()-lower_bound(a[k].begin(),a[k].end(),ned-b[i][j]))%=mod;
}
}
}
} int dep;
void dfs(int v,int res) {
if(!res) {
dep++;
(ans+=cal())%=mod;
return ;
}
if(!v) return ;
dfs(v-1,res);
for(int i=1;i*v<=res;i++) {
num[v]=i;
dfs(v-1,res-i*v);
num[v]=0;
}
} bool cmp(int a,int b) {return a>b;} int main() {
n=Get(),lim=Get();
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
ifac[n]=ksm(fac[n],mod-2);
for(int i=n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
for(int i=0;i<=n;i++)
for(int j=0;j<=i;j++)
c[i][j]=(!j||i==j)?1:(c[i-1][j-1]+c[i-1][j])%mod; for(int i=1;i<=n;i++) a[i]=Get(); for(int i=1;i<=n;i++) {
if(a[i]>0) {
good[++good[0]]=a[i];
sum+=a[i];
} else bad++;
}
ned=sum-lim; if(sum<=lim) {
cout<<ksm(n,n-2)%mod;
return 0;
}
if(!bad) {cout<<0;return 0;} sort(good+1,good+good[0]+1,cmp);
meet_in_the_middle();
g[0]=g[1]=1;
for(int i=2;i<=n;i++) g[i]=ksm(i,i-2)%mod;
dfs(good[0],good[0]);
cout<<ans;
return 0;
}

「2017 山东一轮集训 Day5」苹果树的更多相关文章

  1. 题解 「2017 山东一轮集训 Day5」苹果树

    题目传送门 题目大意 给出一个 \(n\) 个点的图,每个点都有一个权值 \(f_i\) ,如果 \(f_i=-1\) 表示 \(i\) 这个点是坏的.定义一个点是有用的当且仅当它不是坏的,并且它连的 ...

  2. Loj #6073.「2017 山东一轮集训 Day5」距离

    Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...

  3. loj#6073. 「2017 山东一轮集训 Day5」距离(树链剖分 主席树)

    题意 题目链接 Sol 首先对询问差分一下,我们就只需要统计\(u, v, lca(u, v), fa[lca(u, v)]\)到根的路径的贡献. 再把每个点与\(k\)的lca的距离差分一下,则只需 ...

  4. loj#6073. 「2017 山东一轮集训 Day5」距离(费用流)

    题意 题目链接 Sol 我们可以把图行列拆开,同时对于行/列拆成很多个联通块,然后考虑每个点所在的行联通块/列联通块的贡献. 可以这样建边 从S向每个行联通块连联通块大小条边,每条边的容量为1,费用为 ...

  5. 「2017 山东一轮集训 Day5」字符串 (后缀自动机, 拓扑排序)

    /** 首先通过SAM求出每个串本质不同的子串 然后发现转移不好处理整体的本质不同 形如串A可能状态有a,b,ab,空,串B可能状态有b,空两种, 那么我们需要处理ab + 空 和 a + b的情况 ...

  6. 「2017 山东一轮集训 Day5」距离

    /* 写完开店再写这个题目顿时神清气爽, 腰也不疼了, 眼也不花了 首先考虑将询问拆开, 就是查询一些到根的链和点k的关系 根据我们开店的结论, 一个点集到一个定点的距离和可以分三部分算 那么就很简单 ...

  7. 「2017 山东一轮集训 Day5」字符串

    题目 比较神仙的操作啊 首先先考虑一个串的做法,我们有两种:SA或SAM,其中SAM又有两种,拓扑图上的\(dp\)和\(parent\)上随便统计一下 显然这道题\(SA\)和\(parent\)树 ...

  8. LOJ6071. 「2017 山东一轮集训 Day5」字符串 [SAM]

    LOJ 思路 这种计数题显然是要先把每一个合法的串用唯一的方法表示出来.(我连这都没想到真是无可救药了) 如何唯一?容易想到把前缀尽可能多地在第一个串填掉,然后填第二个,第三个-- 如何做到这样?可以 ...

  9. Loj #6069. 「2017 山东一轮集训 Day4」塔

    Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...

随机推荐

  1. [angularjs] angularjs系列笔记(七)HTML DOM

    AngularJs为HTML DOM元素的属性提供了绑定数据的指令 ng-disabled指令 ng-disabled指令直接绑定数据到HTML元素的disabled属性 ng-show指令 ng-s ...

  2. constructor C++ example

    The constructor for this class could be defined, as usual, as:   Rectangle::Rectangle (int x, int y) ...

  3. JavaScript是如何工作的:Web Workers的构建块 + 5个使用他们的场景

    摘要: 理解Web Workers. 原文:JavaScript是如何工作的:Web Workers的构建块 + 5个使用他们的场景 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这 ...

  4. JS中将变量转为字符串

    译者按: 语言的细枝末节了解一下就可以了,不需要太较真,不过如果一点也不知道的话,那就不太妙了. 原文: Converting a value to string in JavaScript 译者:  ...

  5. Java中重写与重载

    重写(覆盖):发生在子类与父类之间:方法名相同方法的个数.类型相同返回值类型小于等于父类的返回值类型 重载:发生在一个类中:方法名相同方法的个数.类型不同返回值类型可以相同也可以不相同

  6. 【20190223】HTTP-知识点整理:HTTPS

    HTTPS:添加了加密及认证机制的HTTP HTTPS 并非是应用层的一种新协议.只是 HTTP 通信接口部分用SSL(Secure Socket Layer)和 TLS(Transport Laye ...

  7. redis redis常用命令及内存分析总结(附RedisClient工具简介

    redis常用命令及内存分析总结(附RedisClient工具简介 by:授客 QQ:1033553122 redis-cli工具 查看帮助 连接redis数据库 常用命令 exists key se ...

  8. Windows服务System权限下在当前用户桌面创建快捷方式C#实例程序

    Windows服务一般运行在System权限下,这样权限比较高,方便执行一些高权限的操作. 但是,Environment.GetFolderPath等函数获取的也是System用户下的,而不是当前用户 ...

  9. 章节四、2-Switch语句

    package introduction5; public class SwitchDemo { //switch用于固定值的判断,如星期.人的性别 //if用于判断区间.范围,能够用switch进行 ...

  10. Nginx 相关介绍(Nginx是什么?能干嘛?)

    Nginx的产生 没有听过Nginx?那么一定听过它的"同行"Apache吧!Nginx同Apache一样都是一种WEB服务器.基于REST架构风格,以统一资源描述符(Unifor ...