省选模拟赛第四轮 B——O(n^4)->O(n^3)->O(n^2)


一
稍微转化一下,就是找所有和原树差距不超过k的不同构树的个数
一个挺trick的想法是:
由于矩阵树定理的行列式的值是把邻接矩阵数值看做边权的图的所有生成树的边权乘积之和
那么如果把不存在于原树中的边的边权设为x,做矩阵树定理得到n-1次的多项式第i次项系数就是选择新选择i个边的方案数!
带着x不好做,x=1~n带入,然后插值即可
O(n^4)
二
开始碾标算了:
还是可以树形DP,经典的树形DP套路难办的原因是不知道干掉的子树接在哪里
所以我们干脆先不管接在哪里,先都砍断
一个公式:
https://rqy.moe/Solutions/bzoj5475/
证明导数第二步有点生成函数的思想了
对于拆成k个连通块,方案数可以直接算
设f[i][j][k],以i为根子树保留j条边,和i相连的连通块sz为k的方案数。枚举子树保留边数和sz就可以愉快转移了
但是会算重
如果直接乘上n^(k-2)可能会把断的边连回去,不止保留了j个,并且同一个方案可能因为保留的不同的j个计算多次!
不慌不慌
一切都是因为实际上可能连回去保留了不止j个,我们钦定了j个,其他随便选的方案数是f,
枚举具体保留了几个,可以二项式反演
$f[t]=\sum_{k=t}^{n} C(k,t)*ans[k]$
ans表示恰好保留了k个
没了
O(n^3)
三
进一步优化!
$\Pi a_i$有组合意义,就是对于k个连通块,我们每个连通块恰好选择一个点的方案数!
f[i][j][0/1],表示,,,,,i这个连通块内有没有选择点,总方案数
愉快转移,断边必须y选择。
代码
:
#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;x=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=;
const int mod=;
int n,k;
int qm(int x,int y){
int ret=;while(y){
if(y&) ret=(ll)ret*x%mod;x=(ll)x*x%mod;
y>>=;
}return ret;
}
struct node{
int nxt,to;
}e[*N];
int hd[N],cnt;
int ad(int x,int y){
return x+y>=mod?x+y-mod:x+y;
}
void add(int x,int y){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
hd[x]=cnt;
}
int sz[N];
int f[N][N][];
int C[N][N];
ll ans[N],dp[N];
void dfs(int x,int fa){
sz[x]=;
f[x][][]=f[x][][]=;
int g[][N][];
memset(g[],,sizeof g[]);
//memset(g,0,sizeof g);
g[][][]=g[][][]=;
int tmp=;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
dfs(y,x);
tmp^=;
//for(reg j=0;j<sz[x]+sz[y];++j) g[tmp][j][0]=g[tmp][j][1]=0;
memset(g[tmp],,sizeof g[tmp]);
for(reg j=;j<sz[x];++j){
for(reg k=;k<sz[y];++k){
g[tmp][j+k][]=ad(g[tmp][j+k][],(ll)g[tmp^][j][]*f[y][k][]%mod);
g[tmp][j+k][]=ad(g[tmp][j+k][],(ll)g[tmp^][j][]*f[y][k][]%mod); g[tmp][j+k+][]=ad(g[tmp][j+k+][],(ll)g[tmp^][j][]*f[y][k][]%mod);
g[tmp][j+k+][]=ad(g[tmp][j+k+][],ad((ll)g[tmp^][j][]*f[y][k][]%mod,(ll)g[tmp^][j][]*f[y][k][]%mod));
}
}
sz[x]+=sz[y];
}
//cout<<" f "<<x<<endl;
for(reg j=;j<sz[x];++j) {
f[x][j][]=g[tmp][j][],f[x][j][]=g[tmp][j][];
//cout<<j<<" n0 "<<" : "<<f[x][j][0]<<" n1 "<<f[x][j][1]<<endl;
}
}
int main(){
rd(n);rd(k);
if(k==){
puts("");return ;
}
int x;
for(reg i=;i<=n;++i){
rd(x);++x;
add(i,x);add(x,i);
}
dfs(,);
for(reg j=;j<n;++j) {
if(j!=n-) dp[j]=(ll)f[][j][]*qm(n,n-j-);
else dp[j]=;
}
ans[n-]=dp[n-];
C[][]=;
for(reg i=;i<=n;++i){
C[i][]=;
for(reg j=;j<=n;++j){
C[i][j]=(C[i-][j-]+C[i-][j])%mod;
}
}
ll op=ans[n-];
for(reg i=n-;i>=n-k-;--i){
// cout<<" ii "<<i<<" : "<<dp[i]<<endl;
for(reg j=i+;j<n;++j){
dp[i]=(dp[i]+mod-(ll)C[j][i]*ans[j]%mod)%mod;
}
ans[i]=dp[i];
op=(op+dp[i])%mod;
}
printf("%lld",op);
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2019/3/4 19:02:40
*/
思路:
矩阵树:新加k条新边,为了控制边数,所以用x表示边权得到方案数,插值也很漂亮
DP:直接DP难写,受prufer序列的推论启发,考虑对于连通块直接统计,然后二项式反演去重,
组合意义的优化,类似于:[NOI2009]管道取珠
省选模拟赛第四轮 B——O(n^4)->O(n^3)->O(n^2)的更多相关文章
- 【洛谷比赛】[LnOI2019]长脖子鹿省选模拟赛 T1 题解
今天是[LnOI2019]长脖子鹿省选模拟赛的时间,小编表示考的不怎么样,改了半天也只会改第一题,那也先呈上题解吧. T1:P5248 [LnOI2019SP]快速多项式变换(FPT) 一看这题就很手 ...
- @省选模拟赛03/16 - T3@ 超级树
目录 @description@ @solution@ @accepted code@ @details@ @description@ 一棵 k-超级树(k-SuperTree) 可按如下方法得到:取 ...
- 3.28 省选模拟赛 染色 LCT+线段树
发现和SDOI2017树点涂色差不多 但是当时这道题模拟赛的时候不会写 赛后也没及时订正 所以这场模拟赛的这道题虽然秒想到了LCT和线段树但是最终还是只是打了暴力. 痛定思痛 还是要把这道题给补了. ...
- NOI2019省选模拟赛 第五场
爆炸了QAQ 传送门 \(A\) \(Mas\)的童年 这题我怎么感觉好像做过--我记得那个时候还因为没有取\(min\)结果\(100\to 0\)-- 因为是个异或我们肯定得按位考虑贡献了 把\( ...
- NOI2019省选模拟赛 第六场
传送门 又炸了-- \(A\) 唐时月夜 不知道改了什么东西之后就\(A\)掉了\(.jpg\) 首先,题目保证"如果一片子水域曾经被操作过,那么在之后的施法中,这片子水域也一定会被操作&q ...
- 省选模拟赛 arg
1 arg (arg.cpp/in/out, 1s, 512MB)1.1 Description给出一个长度为 m 的序列 A, 请你求出有多少种 1...n 的排列, 满足 A 是它的一个 LIS. ...
- 5.10 省选模拟赛 拍卖 博弈 dp
LINK:拍卖 比赛的时候 前面时间浪费的有点多 写这道题的时候 没剩多少时间了. 随便设了一个状态 就开始做了. 果然需要认真的思考.其实 从我的状态的状态转移中可以看出所有的结论. 这里 就不再赘 ...
- 5.5 省选模拟赛 B Permutation 构造 贪心
LINK:Permutation 对于这种构造神题 我自然是要补的.为啥就我没想出来哇. 30分还是很好写的 注意8!实际上很小 不需要爆搜 写bfs记录状态即可.至于判断状态是否出现与否 可以开ma ...
- 省选模拟赛 4.26 T1 dp 线段树优化dp
LINK:T1 算是一道中档题 考试的时候脑残了 不仅没写优化 连暴力都打挂了. 容易发现一个性质 那就是同一格子不会被两种以上的颜色染.(颜色就三种. 通过这个性质就可以进行dp了.先按照左端点排序 ...
随机推荐
- YouTube视频下载的12个软件(Win和Mac)
如今,观看视频已经成为人们生活中重要的一部分.很多时候,我们都需要用到视频,比如教育用途.会议报告.休闲娱乐以及广告宣传等.如果你觉得有时候资源不好找的话,不放去看下YouTube.YouTube是世 ...
- SpringMvc返回Json调试
spring-web-5.0.6.RELEASE.jar!/org/springframework/web/method/support/HandlerMethodReturnValueHandler ...
- 洛谷P1004 方格取数-四维DP
题目描述 设有 N \times NN×N 的方格图 (N \le 9)(N≤9) ,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 00 .如下图所示(见样例): A 0 0 0 0 0 ...
- Git学习笔记 第二章
文件相关操作 修改readme.txt文件,执行 git status 命令查看当前仓库状态 git status 位于分支 master 尚未暂存以备提交的变更: (使用 "git add ...
- Genymotion安装总结
周末的时候为了测试论文中的Almond虚拟助手软件,所以要去Google Play上去下载. 但是我的两个安卓模拟器:夜神和海马玩模拟器的安卓版本太低了,导致无法使用 谷歌服务,所以连商店都进不去. ...
- 在VS2017上对C++项目进行单元测试
操作系统:win10 VS2017安装:http://www.cnblogs.com/Metak/p/7471671.html 参考博客: http://blog.csdn.net/lovehaiho ...
- 为什么要用Thrift
Why Thrift, Why not HTTP RPC(JSON+gzip) https://stackoverflow.com/questions/9732381/why-thrift-why-n ...
- Java日志输出问题
以前有一个同事,说自己的Java控制台程序,输出的信息,打印信息以及错误信息,在windows的command line刷屏,想复制下来,想要自输出到日志文件里. 自己写文件太麻烦,他从网上只找到用重 ...
- Java与JavaScript 完美实现字符串拆分(利用数组存储)与合并的互逆操作
Java: String typeStr = "1,2"; String[] typeArray = typeStr.split(","); typeStr = ...
- Python表达式与运算符
表达式与运算符 Python语言支持以下类型的运算符: 算术运算符 比较(关系)运算符 赋值运算符 逻辑运算符 位运算符 成员运算符 身份运算符 运算符优先级 算术运算符 运算符 描述 + 加 - 两 ...