【拉格朗日优化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. OpenIM Open Source Instant Messaging Project Docker Compose Deployment Guide

    The deployment of OpenIM involves multiple components and supports various methods including source ...

  2. vim 从嫌弃到依赖(11)——标签页操作

    各位小伙伴们,你们好,我又更新了! 之前介绍了vim关于多窗口的操作,vim中多窗口是将多个窗口在一个屏幕中显示,这似乎与我们常见的ide有点不一样,一般的ide都是新开一个窗口之后在新的标签页中显示 ...

  3. 深度学习应用篇-计算机视觉-OCR光学字符识别[7]:OCR综述、常用CRNN识别方法、DBNet、CTPN检测方法等、评估指标、应用场景

    深度学习应用篇-计算机视觉-OCR光学字符识别[7]:OCR综述.常用CRNN识别方法.DBNet.CTPN检测方法等.评估指标.应用场景 1.OCR综述 OCR(Optical Character ...

  4. 19.8 Boost Asio 异或加密传输

    异或加密是一种对称加密算法,通常用于加密二进制数据.异或操作的本质是对两个二进制数字进行比较,如果它们相同则返回0,如果不同则返回1.异或加密使用一把密钥将明文与密文进行异或运算,从而产生密文.同时, ...

  5. 从嘉手札<2023-11-18>

    随便补一个~ 1.我也不是不快乐,我其实挺快乐的,和朋友出去玩,看电影,刷搞笑视频,我都能表现的很好,但这些都不是真正让我感受到快乐的东西,它就像膝跳反应一样,碰我一下我就会笑,但笑完就结束了.甚至在 ...

  6. Mybatis(一对一、一对多、多对多)操作

    * 首先列出示例中用到的数据库表 user表: accout表: role表: user_role表: 建表语句如下: DROP TABLE IF EXISTS `user`; CREATE TABL ...

  7. 【开工大吉】推荐4款开源、美观的WPF UI组件库

    前言 经常有小伙伴在技术群里提问:WPF有什么好用的UI组件库?,今天大姚给大家推荐4款开源.美观的WPF UI组件库. WPF介绍 WPF 是一个强大的桌面应用程序框架,用于构建具有丰富用户界面的 ...

  8. 从零开始的react入门教程(三),了解react事件与使用注意项

    壹 ❀ 引 在从零开始的react入门教程(二),从react组件说到props/state的联系与区别一文中,我们介绍了react组件的基本用法以及props与state的区别.其中react组件分 ...

  9. 【Unity3D】UGUI之InputField

    1 InputField 属性面板 ​ 在 Hierarchy 窗口右键,选择 UI 列表里的 InputField(输入框)控件,即可创建 InputField 控件,选中创建的 InputFiel ...

  10. letcode-两数相除

    题解 设未知数: Br= 125 / 3,拆进行如下拆解: Br = 125 / 3 Br = (29 + 96)/3 Br = 29/3 + (32 * 3) / 3 Br = 29/3 + (2 ...