【拉格朗日优化dp】P4365 [九省联考 2018] 秘密袭击 coat

题目简述

  • 求树上所有连通块第 \(k\) 大点权(不足 \(k\) 点记为 \(0\))的和。
  • \(1\leq k\leq n\leq1666\),\(d_i\leq W\leq 1666\)。

解题思路

我们对第 \(k\) 大的和的这种问题,通常的处理方法是对值域枚举一个阈值 \(i\)。对于 \(\geq i\) 的点视作为 \(1\),\(< i\) 的点视作为 \(0\)。这样子问题就变成了了所有 \(i\),连通块第 \(k\) 大是 \(1\)(也就是有 \(\geq k\) 个 \(1\))的数目的和。

假设 \(f[u][j]\) 表示连通块以 \(u\) 为根且有 \(j\) 个 \(1\) 节点,我们可以把问题转成树上背包。

通过经典结论,合理控制枚举范围我们可以做到 \(O(n^2)\) 来转移,总得时间复杂度 \(O(Wn^2)\),随便常数优化一下跑得比正解还要快。

我们下面讨论正解。

众所周知的结论,树上背包可以转为生成函数,也就是说假设 \(F_u(x)=f[u][0]+f[u][1]x+f[u][2]x^2+\dots\)。那么就会有:

\[F_u(x)=x^{d[u]\geq i}\prod_{v\in u} (F_v(x)+1)
\]

多项式卷积很慢,而且不便于转移,所以我们不应该卷积,考虑点值表示法。

假设我们选取的点为 \(t\),那么转移变为:

\[F_u=t^{d[u]\geq i}\prod_{v\in u}(F_v+1)
\]

不难发现阈值 \(i\) 对整个 \(F\) 的影响比较小,我们不妨考虑换一种思考方式,也就是枚举点 \(t\),然后快速搞定每一个阈值。我们期望每一次枚举 \(t\) 可以做到 \(O(n\log W)\) 得到所有阈值的值。

我们考虑每个位置维护一个当前值数组 \(val\),那么每一个节点相当于是他所有叶节点的对应 \(val\) 位 \(+1\) 再相乘,然后再给 \([1,d[u]]\) 的位乘上 \(t\)。暴力乘积很慢,但是可以发现我们需要维护的东西是:

  • 全局加。
  • 区间乘。
  • 合并两个 \(val\),对应位相乘。

这个结构是很显然的线段树合并,这里就可以做到 \(O(n\log W)\) 得到所有阈值了。

还没完,有一个小的问题,就是一个连通块的根不是 \(1\) 时由于线段树合并了,所以他的 \(val\) 会被他的祖先修改掉导致不可用,解决这个问题,我们需要一个辅助生成函数 \(G_u(x)\) 表示所有 \(u\) 的子树节点的 \(val\),那我们的 \(G(x)\) 需要维护一个:

  • 合并两个 \(G\),对应位相加。
  • 把 \(F\) 的对应位相加到 \(G\)。

感觉对 \(G\) 单独再开个动态开点线段树可以做,但是写起来超级麻烦,介绍一个相对简单的方法。

考虑矩阵乘法:

\[\begin{bmatrix}f&g&1\end{bmatrix}\times \begin{bmatrix}a&b&0\\0 &1&0\\c&d&1\end{bmatrix}=\begin{bmatrix}af+c&bf+g+d&1\end{bmatrix}
\]

矩阵 \(\begin{bmatrix}a&b&0\\0 &1&0\\c&d&1\end{bmatrix}\) 具有可乘形式,具体来说:

\[\begin{bmatrix}a&b&0\\0 &1&0\\c&d&1\end{bmatrix}\times \begin{bmatrix}a'&b'&0\\0 &1&0\\c'&d'&1\end{bmatrix}=\begin{bmatrix}aa'&ab'+b&0\\0 &1&0\\ca'+c'&cb'+d+d'&1\end{bmatrix}
\]

我们可以维护这种矩阵,可以发现这种矩阵可以维护:

  • 全局加 \(1\):\(a=c=1,b=d=0\)。
  • 把 \(f\) 加到 \(g\) 上:\(a=b=1,c=d=0\)。

其他操作需要线段树合并。我们就完成了本题的求点值部分。

对于转回多项式,我们有拉格朗日插值法:

\[f(x)=\sum_{i=1}^n y_i\prod_{j\not =i} \dfrac{x-j}{i-j}
\]

不难发现这个式子每个 \(i\) 的常数可以先预处理出来,多项式卷积部分我们可以先求暴力 \(\prod(x-j)\) 然后每次除 \(x-i\) 即可,暴力除是 \(O(n)\) 的,原不及瓶颈线段树合并的大常数 \(O(n^2\log n)\)。

参考代码

#include<iostream>
#include<vector>
using namespace std;
#define ll long long
const int MOD=64123;
const int MAXN=1667;
int n,k,w;
ll d[MAXN],f[MAXN];
ll fac[MAXN],inf[MAXN];
int rt[MAXN];
vector<int> ve[MAXN];
int add(int x){return (x%MOD+MOD)%MOD;}
ll inv(ll a,int b=MOD-2){ll res=1;while(b){if(b&1) res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
struct matrix{
ll a,b,c,d;
matrix(ll aa=0,ll bb=0,ll cc=0,ll dd=0){a=aa,b=bb,c=cc,d=dd;}
matrix operator *(matrix i){return matrix(1ll*a*i.a%MOD,add(1ll*i.a*b%MOD+i.b),
add(1ll*a*i.c%MOD+c),add(1ll*b*i.c%MOD+add(d+i.d)));}
bool operator ==(matrix i){return (i.a==a)&&(i.b==b)&&(i.c==c)&&(i.d==d);}
};
matrix I=(matrix){1,0,0,0};
struct node{
int ls,rs;
matrix val;
}t[MAXN*40];int tot=0;
struct poly{
vector<ll> ve;
void init(int s){ve.resize(s);}
poly operator +(poly x){
poly y;y.init(max(x.ve.size(),ve.size()));
for(int i=0;i<y.ve.size();i++)
y.ve[i]=add(((i<ve.size())?ve[i]:0)+
((i<x.ve.size())?x.ve[i]:0));
return y;
}
poly operator *(ll x){
poly y;y.init(ve.size());
for(int i=0;i<ve.size();i++)
y.ve[i]=ve[i]*x%MOD;
return y;
}
poly operator *(poly x){
poly y;y.init(x.ve.size()+ve.size()-1);
for(int i=0;i<x.ve.size();i++)
for(int j=0;j<ve.size();j++)
y.ve[i+j]=add(y.ve[i+j]+x.ve[i]*ve[j]%MOD);
return y;
}
poly operator /(ll x){
poly y;y.init(ve.size()-1);
ll res=ve[ve.size()-1];
for(int i=ve.size()-2;i>=0;i--){
y.ve[i]=res;
res=add(ve[i]+x*res%MOD);
}
return y;
}
};
void newnode(int &k){k=++tot;t[k]=node{0,0,I};}
void pushdown(int k){
if(t[k].val==I) return;
if(!t[k].ls) newnode(t[k].ls);
if(!t[k].rs) newnode(t[k].rs);
t[t[k].ls].val=t[t[k].ls].val*t[k].val;
t[t[k].rs].val=t[t[k].rs].val*t[k].val;
t[k].val=I;
}
void modify(int &id,int l,int r,int L,int R,matrix val){
if(l>R||r<L) return;
if(!id) newnode(id);
if(L<=l&&r<=R) {t[id].val=t[id].val*val;return;}
int mid=l+r>>1;
pushdown(id);
modify(t[id].ls,l,mid,L,R,val);
modify(t[id].rs,mid+1,r,L,R,val);
}
void merge(int &x,int y,int l,int r){
if(!t[x].ls&&!t[x].rs) swap(x,y);
if(!t[y].ls&&!t[y].rs){
t[x].val=t[x].val*matrix{t[y].val.b,0,0,t[y].val.d};
return;
}
int mid=l+r>>1;
pushdown(x);pushdown(y);
merge(t[x].ls,t[y].ls,l,mid);
merge(t[x].rs,t[y].rs,mid+1,r);
}
void dfs(int u,int fa,int x){
modify(rt[u],1,w,1,d[u],matrix{0,x,0,0});
modify(rt[u],1,w,d[u]+1,w,matrix{0,1,0,0});
for(int v:ve[u]) if(v!=fa) {
dfs(v,u,x);
modify(rt[v],1,w,1,w,matrix{1,1,0,0});
merge(rt[u],rt[v],1,w);
}
modify(rt[u],1,w,1,w,matrix{1,0,1,0});
return;
}
int query(int id,int l,int r){
if(l==r) return t[id].val.d;
int mid=l+r>>1;
pushdown(id);
return add(query(t[id].ls,l,mid)+query(t[id].rs,mid+1,r));
}
signed main(){
cin>>n>>k>>w;
for(int i=1;i<=n;i++)
cin>>d[i];
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
ve[u].push_back(v);
ve[v].push_back(u);
}
for(int i=0;i<=n;i++){
tot=0;
for(int j=1;j<=n;j++) rt[j]=0;
dfs(1,0,i);
f[i]=query(rt[1],1,w);
}
poly p;p.init(1);p.ve[0]=1;
poly v;v.init(2);v.ve[1]=1;
for(int i=0;i<=n;i++){
v.ve[0]=(MOD-i)%MOD;
p=p*v;
}
poly F;F.init(n+1);
fac[0]=inf[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%MOD,inf[i]=inv(fac[i]);
for(int i=0;i<=n;i++){
int val=1ll*f[i]*inf[i]%MOD*inf[n-i]%MOD;
if((n-i)&1) val=MOD-val;
poly y=(p/i)*val;
F=F+y;
}
int res=0;
for(int i=k;i<=n;i++)
res=add(res+F.ve[i]);
cout<<res;
return 0;
}

【拉格朗日优化dp】P4365 [九省联考 2018] 秘密袭击 coat的更多相关文章

  1. P4365 [九省联考2018]秘密袭击coat

    $ \color{#0066ff}{ 题目描述 }$ Access Globe 最近正在玩一款战略游戏.在游戏中,他操控的角色是一名C 国士 兵.他的任务就是服从指挥官的指令参加战斗,并在战斗中取胜. ...

  2. luogu P4365 [九省联考2018]秘密袭击coat

    luogu 这里不妨考虑每个点的贡献,即求出每个点在多少个联通块中为第\(k\)大的(这里权值相同的可以按任意顺序排大小),然后答案为所有点权值\(*\)上面求的东西之和 把比这个点大的点看成\(1\ ...

  3. [九省联考2018]秘密袭击coat

    [九省联考2018]秘密袭击coat 研究半天题解啊... 全网几乎唯一的官方做法的题解:链接 别的都是暴力.... 要是n=3333暴力就完了. 一.问题转化 每个联通块第k大的数,直观统计的话,会 ...

  4. 并不对劲的复健训练-bzoj5250:loj2473:p4365:[九省联考2018]秘密袭击

    题目大意 有一棵\(n\)(\(n\leq 1666\))个点的树,有点权\(d_i\),点权最大值为\(w\)(\(w\leq 1666\)).给出\(k\)(\(k\leq n\)),定义一个选择 ...

  5. 解题:九省联考2018 秘密袭击CoaT

    题面 按照*Miracle*的话来说,网上又多了一篇n^3暴力的题解 可能是因为很多猫题虽然很好,但是写正解性价比比较低? 直接做不可做,转化为统计贡献:$O(n)$枚举每个权值,直接统计第k大大于等 ...

  6. [九省联考 2018]秘密袭击coat

    Description 题库链接 给出一棵 \(n\) 个点的树,每个点有点权.求所有联通块的权值 \(k\) 大和,对 \(64123\) 取模. \(1\leq n,k\leq 1666\) So ...

  7. [LOJ #2473] [九省联考2018] 秘密袭击coat

    题目链接 洛谷. LOJ,LOJ机子是真的快 Solution 我直接上暴力了...\(O(n^2k)\)洛谷要\(O2\)才能过...loj平均单点一秒... 直接枚举每个点为第\(k\)大的点,然 ...

  8. 【BZOJ5250】[九省联考2018]秘密袭击(动态规划)

    [BZOJ5250][九省联考2018]秘密袭击(动态规划) 题面 BZOJ 洛谷 给定一棵树,求其所有联通块的权值第\(k\)大的和. 题解 整个\(O(nk(n-k))\)的暴力剪剪枝就给过了.. ...

  9. [BZOJ5250][九省联考2018]秘密袭击(DP)

    5250: [2018多省省队联测]秘密袭击 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 3  Solved: 0[Submit][Status][D ...

  10. LuoguP4365 [九省联考2018]秘密袭击

    https://zybuluo.com/ysner/note/1141136 题面 求一颗大小为\(n\)的树取联通块的所有方案中,第\(k\)个数之和. \(n\leq1,667,k\leq n\) ...

随机推荐

  1. layui之静态表格的分页及搜索功能以及前端使用XLSX导出Excel功能

    LayUI官方文档:https://layui.dev/docs/2/#introduce XLSX NPM地址:https://www.npmjs.com/package/xlsx XLSX 使用参 ...

  2. SqlSugar的查询函数SqlFunc

    用法 我们可以使用SqlFunc这个类调用Sql函数,用法如下: db.Queryable<Student>().Where(it => SqlFunc.ToLower(it.Nam ...

  3. ElasticSearch-索引库、文档操作

    1.elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容 2.elasticsearch和lucen ...

  4. VB6的WindowsXP控件引擎 - 开源研究系列文章

    这几天翻了一下原来VB6的代码,将一些有用的代码进行了整理,然后将这些代码记录下来,开源出来,让需要的朋友能够进行代码复用. 这次介绍的是一个VB6的WindowXP的控件引擎代码,主要是在程序启动的 ...

  5. 体验 ABP 的功能和服务

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. ABP是一个全栈开发框架,它在企业解决方案的各个方面都有许多构建模块.在前面三章中 ...

  6. idea破解《当脚本破解方式无效或不方便执行时可采用此方法》

    idea新版破解有时会各种不成功,很耽误事.所以,再次整理一个相对省事有效的办法.<此方式为修改idea启动脚本破解方式>内容如下: 一:下载此激活工具 二:按下图从下载的文件中找到箭头标 ...

  7. WDMyCloud的ssh登陆密码错误

    是一个困扰很久的小问题,以至于无法ssh登陆到NAS服务器进行操作. 之前配置是可以直接使用root用户ssh登陆到服务器的,可是后来突然就不行了,无论输入啥密码都是报错: Permission de ...

  8. 【CAS学习二】CAS部署和联调

    上一篇写到服务端部署的是CAS 6.4版本,可后面与客户端集成时出现未认证授权的服务,如下: 网上查了下,要把http的访问打开.具体设置步骤是:修:%Tomcat%\webapps\cas\WEB- ...

  9. Python 爬虫方法总结

    实现爬虫的套路 准备URL 准备start_url url地址规律不明显,总数不确定 通过代码提取下一页的url 通过xpath提取 寻找url地址,部分参数在当前的响应中(比如当前页码数和总页码数在 ...

  10. DP的各种优化小结

    动态规划算法(简称动规,DP),是IO中最为常见的,也是最为重要的算法之一.这也就意味着,在各种题目与比赛中它会有很多稀奇古怪的算法和优化,时不时地在你的面前出现一个TLE,MLE和RE来搞你的心态. ...