省选模拟赛第四轮 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了.先按照左端点排序 ...
随机推荐
- JDK8漫谈——代码更优雅
简介 lambda表达式,又称闭包(Closure)或称匿名方法(anonymous method).将Lambda表达式引入JAVA中的动机源于一个叫"行为参数"的模式.这种模式 ...
- JavaScript如何实现继承
// 原型方式的'继承' function Person(name) { //定义一个Person的构造函数 this.name = name; //添加属性 } Person.prototype.s ...
- left join 右表数据不唯一的情况解决方法
https://blog.csdn.net/u010089432/article/details/52165026
- 【Beta阶段】第六次Scrum Meeting!
每日任务内容: 本次会议为第六次Scrum Meeting会议~ 由于本次会议项目经理召开时间为9:30,在公寓1楼会客室召开,召开时长约30分钟,探讨了本次取得的重大突破后需要继续开展的工作. 队员 ...
- 12.25daily_scrum
今天是圣诞节,大家在度过了一个愉快的节日同时,同时也收到了最好的圣诞礼物,就是调试工作已经进入尾声,接下来我们组的主要任务就是M2阶段的总结了.为了更好的做好M2阶段的收官工作,我们组决定分配相当的一 ...
- Linux内核总结博客 20135332武西垚
http://www.cnblogs.com/wuxiyao/p/5220677.htmlhttp://www.cnblogs.com/wuxiyao/p/5247571.htmlhttp://www ...
- Leetcode 546. Remove Boxes
题目链接: https://leetcode.com/problems/remove-boxes/description/ 问题描述 若干个有序排列的box和它们的颜色,每次可以移除若干个连续的颜色相 ...
- Java与JavaScript 完美实现字符串拆分(利用数组存储)与合并的互逆操作
Java: String typeStr = "1,2"; String[] typeArray = typeStr.split(","); typeStr = ...
- ionic动态切换主题皮肤
本来想通过css自定义属性值: :root{ --red:red; } p{ color:var(--red); } div{ background:var(--red); } 在ionic2设置动态 ...
- python之tkinter使用-Grid(网格)布局管理器
# 使用tkinter编写登录窗口 # Grid(网格)布局管理器会将控件放置到一个二维的表格里,主控件被分割为一系列的行和列 # stricky设置对齐方式,参数N/S/W/E分别表示上.下.左.右 ...