【拉格朗日优化dp】P4365 [九省联考 2018] 秘密袭击 coat
【拉格朗日优化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\)。那么就会有:
\]
多项式卷积很慢,而且不便于转移,所以我们不应该卷积,考虑点值表示法。
假设我们选取的点为 \(t\),那么转移变为:
\]
不难发现阈值 \(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}a&b&0\\0 &1&0\\c&d&1\end{bmatrix}\) 具有可乘形式,具体来说:
\]
我们可以维护这种矩阵,可以发现这种矩阵可以维护:
- 全局加 \(1\):\(a=c=1,b=d=0\)。
- 把 \(f\) 加到 \(g\) 上:\(a=b=1,c=d=0\)。
其他操作需要线段树合并。我们就完成了本题的求点值部分。
对于转回多项式,我们有拉格朗日插值法:
\]
不难发现这个式子每个 \(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的更多相关文章
- P4365 [九省联考2018]秘密袭击coat
$ \color{#0066ff}{ 题目描述 }$ Access Globe 最近正在玩一款战略游戏.在游戏中,他操控的角色是一名C 国士 兵.他的任务就是服从指挥官的指令参加战斗,并在战斗中取胜. ...
- luogu P4365 [九省联考2018]秘密袭击coat
luogu 这里不妨考虑每个点的贡献,即求出每个点在多少个联通块中为第\(k\)大的(这里权值相同的可以按任意顺序排大小),然后答案为所有点权值\(*\)上面求的东西之和 把比这个点大的点看成\(1\ ...
- [九省联考2018]秘密袭击coat
[九省联考2018]秘密袭击coat 研究半天题解啊... 全网几乎唯一的官方做法的题解:链接 别的都是暴力.... 要是n=3333暴力就完了. 一.问题转化 每个联通块第k大的数,直观统计的话,会 ...
- 并不对劲的复健训练-bzoj5250:loj2473:p4365:[九省联考2018]秘密袭击
题目大意 有一棵\(n\)(\(n\leq 1666\))个点的树,有点权\(d_i\),点权最大值为\(w\)(\(w\leq 1666\)).给出\(k\)(\(k\leq n\)),定义一个选择 ...
- 解题:九省联考2018 秘密袭击CoaT
题面 按照*Miracle*的话来说,网上又多了一篇n^3暴力的题解 可能是因为很多猫题虽然很好,但是写正解性价比比较低? 直接做不可做,转化为统计贡献:$O(n)$枚举每个权值,直接统计第k大大于等 ...
- [九省联考 2018]秘密袭击coat
Description 题库链接 给出一棵 \(n\) 个点的树,每个点有点权.求所有联通块的权值 \(k\) 大和,对 \(64123\) 取模. \(1\leq n,k\leq 1666\) So ...
- [LOJ #2473] [九省联考2018] 秘密袭击coat
题目链接 洛谷. LOJ,LOJ机子是真的快 Solution 我直接上暴力了...\(O(n^2k)\)洛谷要\(O2\)才能过...loj平均单点一秒... 直接枚举每个点为第\(k\)大的点,然 ...
- 【BZOJ5250】[九省联考2018]秘密袭击(动态规划)
[BZOJ5250][九省联考2018]秘密袭击(动态规划) 题面 BZOJ 洛谷 给定一棵树,求其所有联通块的权值第\(k\)大的和. 题解 整个\(O(nk(n-k))\)的暴力剪剪枝就给过了.. ...
- [BZOJ5250][九省联考2018]秘密袭击(DP)
5250: [2018多省省队联测]秘密袭击 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 3 Solved: 0[Submit][Status][D ...
- LuoguP4365 [九省联考2018]秘密袭击
https://zybuluo.com/ysner/note/1141136 题面 求一颗大小为\(n\)的树取联通块的所有方案中,第\(k\)个数之和. \(n\leq1,667,k\leq n\) ...
随机推荐
- 4.4 Windows驱动开发:内核监控进程与线程创建
当你需要在Windows操作系统中监控进程的启动和退出时,可以使用PsSetCreateProcessNotifyRoutineEx函数来创建一个MyCreateProcessNotifyEx回调函数 ...
- 遥感图像处理笔记之【Сrор field boundary detection: approaches and main challenges】
遥感图像处理学习(6) 前言 遥感系列第6篇.遥感图像处理方向的学习者可以参考或者复刻 本文初编辑于2023年12月16日 2024年1月24日搬运至本人博客园平台 文章标题:Сrор field b ...
- 使用window.print进行前端打印,批量打印,设置分页,ie、火狐下设置页眉页脚
window.print() print() 方法用于打印当前窗口的内容.谷歌调用 print() 方法会产生一个打印预览弹框,让用户可以设置打印请求. 但谷歌貌似不能自定义设置页眉页脚的文字:ie和 ...
- VB6的OfficeMenu控件 - 开源研究系列文章
这次将原来VB6中喜欢和使用到的OfficeMenu的控件做一个使用介绍. 上次介绍了VB6中的控件引擎,但是那个只针对基本的控件,这个OfficeMenu控件在当时是收费的,笔者找度娘好不容易才下载 ...
- 分享实用小工具:JAVA版本位运算工具类
将二进制数中的每位数字1或0代表着某种开关标记,1为是,0为否,则一个数字可以代表N位的开关标记值,可有效减少过多的变量定义 或 过多的表字段,同时也能在一些复杂的组合判断场景下利用位与.位或.异或等 ...
- ehlib组件包当中TDBLookupComboboxEh的小结
TDBLookupComboboxEh和TDBGridEh一样强大无比,可以做出Combobox下拉出Grid的效果.下面是一些重要属性的小结(可怜费了我半天功夫,文档太少了.......)(1)Li ...
- CentOS7.5上卸载Oracle19c
最近遇到一个麻烦的事情,由于公司开发的数据库备份容灾系统,对于备份容灾的数据库必须使用LVM(pv.vg.lv),所以之前安装的Oracle19C 必须卸载掉,然后使用空白磁盘通过parted.fdi ...
- [Redis] Redis的三大缓存异常原因分析和解决方案
Redis的三大缓存异常原因分析和解决方案 缓存的三个异常分别是缓存击穿.缓存雪崩.缓存穿透.这三个问题一旦发生,会导致大量的请求积压到数据库层,并发量巨大的情况下很有可能导致数据库宕机或是故障,造成 ...
- SpringBoot中单元测试如何对包含AopContext.currentProxy()的方法进行测试
今天在工作中遇到一个问题,一个Service类中有一个方法,其中使用了 AopContext.currentProxy() 去访问自身的函数,例如 int result = ((OrderServic ...
- 【译】.NET 8 网络改进(一)
原文 | Máňa,Natalia Kondratyeva 翻译 | 郑子铭 随着新的 .NET 版本的发布,发布有关网络空间中新的有趣变化的博客文章已成为一种传统.今年,我们希望引入 HTTP 空间 ...