【拉格朗日优化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\) ...
随机推荐
- C/C++ 发送与接收HTTP/S请求
HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议.它是一种无状态的.应用层的协议,用于在计算机之间传输超文本文档,通常在 Web 浏览器和 Web 服务器之 ...
- npm查看插件所有版本命令
npm view webpack versions npm view webpack versions
- Ubuntu 23.04 正式发布
Ubuntu 23.04 "Lunar Lobster" 是 Ubuntu 操作系统的最新短期支持版本,该版本将获得 9 个月的支持,直到 2024 年 1 月.如果你需要长期支持 ...
- PHP使用cookie做浏览历史记录
/** * @param $article文章详情 * @param int $count记录数 * tp须引入cookie类 */ function addHistory($article,$cou ...
- 【题解】P5461 赦免战俘
一.题目 现有 \(2^n\times2^n\ (n≤10)\) 名作弊者站成一个正方形方阵等候 kkksc03 的发落.kkksc03 决定赦免一些作弊者.他将正方形矩阵均分为 4 个更小的正方形矩 ...
- 20.2 显示的链接到导出符号--《Windows核心编程》
FAPPROC GetProcAddress(HMOUDLE hInstDll,PCSTR pszSymbolName); 1.根据名称 FARPROC FunctionAddress = (ULON ...
- 《ASP.NET Core 与 RESTful API 开发实战》-- (第6章)-- 读书笔记(下)
第 6 章 高级查询和日志 6.3 排序 RESTful API 在实现排序时应支持对集合资源的一个或多个属性进行排序 示例对 authors 资源按照其属性 Age 升序排序,再按 BirthPla ...
- HASHTEAM香山杯2023WP
目录 前言 misc 签到题 web PHP_unserialize_pro Re URL从哪儿来 hello python pwn Move pwthon 附上c-python调试方法 crypto ...
- Pandas处理股票数据
import tushare as ts import pandas as pd # 下载茅台所有股票交易数据 # df = ts.get_k_data(code="600519" ...
- NC51216 花店橱窗
题目链接 题目 题目描述 小q和他的老婆小z最近开了一家花店,他们准备把店里最好看的花都摆在橱窗里. 但是他们有很多花瓶,每个花瓶都具有各自的特点,因此,当各个花瓶中放入不同的花束时,会产生不同的美学 ...