P17851
Gaochenxi103_QAQ
·
·
题解
沙子卡常题,差评。
做法
考虑本题弱化版,允许 O(n\log n) 复杂度如何处理。笔者题解。
定义 dp_{u,i} 表示子树 u 内选择出 i 条不相交的路径的最大边权和,钦定 i>0 时 u 必选。
观察到这个路径选择收益是递减的,即从选 i 条提升到选 i+1 条带来的额外收益是不小于从选 i+1 条到选 i+2 条的。即 dp_{u}(i+1)-dp_u(i)\ge dp_{u}(i+2)-dp_{u}(i+1),令 d_i=dp_u(i+1)-dp_u(i)。d_i 单调不增,故 dp_u 是上凸的。
合并子树 v 的时候有两种情况,一种是不考虑 v 然后直接做 (\max,+) 卷积合并。一种是 dp_u(i)=\max(dp_u(i),dp_{v}(i)+w),注意到这个只等价于 dp_u(0) 和 dp_v(i) 合并的时候多了 w 的价值,于是在卷积前令 dp_u(0)=w,卷完之后 dp_u(0) 变回 0 即可。这样就完成了归一。
但让 dp_u(0)=w,相当于 d_0-w。这可能会导致 d_i 不在单调,也就是会影响 dp_u 的凸性。但如果我们令 v 的合并顺序是按照 w 从大到小的。那么 dp_u(0) 在赋值为 w 后依旧是上凸的。
::::info[证明]
要证 dp_u(0) 从 0\to w 后 dp_u(i) 依旧上凸。即证 d_0-w\ge d_1,d_0-d_1\ge w。
考虑使用数学归纳法。
考虑在合并第一个 v 的时候。dp_u(1) 只可能等于 dp_v(1)+w,同理 dp_u(2)=dp_v(2)+w,以此类推。此时 d_i=dp_v(i+1)-dp_v(i),u 的 d_i 此时与 v 的 d_i 完全相同。
然后因为是 u 考虑的第一个儿子,所以没有之前的状态。所有非 0 状态要加上价值 w,dp_u(i)=dp_v(i)+w,i> 0。差分数组中等价于只有 d_0\gets d_0-w。此前 d_i 完全等于 v 的 d_i,满足 d_i\ge d_{i+1}。所以第一次操作后 d_0-d_1\ge w,之后的操作都满足 w 不大于第一次操作的。临时 d_0-w 不会让 d_0<d_1,单调性仍存在。
---
发现每次除了 $d_0$ 以外,其余的 $d_i$ 都只是参与合并不会修改值。且最后我们也只关注 $1$ 的前 $m$ 个的 $d_i$ 和。于是每个点只维护 $d_0$,每次合并之后较小的一方将 $d_0$ 存到一个东西 $T$ 里面,因为它最后一定会在 $1$ 的 $d_i$ 里面。具体第几个目前不重要。
最后再将 $d_0$ 加入全局 $T$ 里面,$T$ 就是 $1$ 未排序的 $d_i$ 集。用`nth_element`可以做到 $O(n)$ 的时间将前 $k$ 小的放在前 $k$ 个位置。但不保证前 $k$ 个之间的大小关系。合并和统计就做到了线性。
然后考虑 $v$ 要按照 $w$ 从大到小处理。本题 $w\le 2^{17}$,直接用桶计数排序即可。
::::info[详细处理]
```cpp
for(int i=2;i<=n;i++)tot[W[i]^X[i&3]]++;//统计每个值出现次数
for(int i=(1<<17);i>0;i--)cnt[i-1]=cnt[i]+tot[i];//cnt[i] 表示最终排序玩之后前 [1,cnt[i]] 的数都大于 i
for(int i=2;i<=n;i++)ord[++cnt[W[i]^X[i&3]]]=i;//大小为 i 的值从 cnt[i] 开始放在排序后的数组里面
for(int i=1;i<n;i++)//按照 w 排序后的顺序加入图即可
{
int v=ord[i];int u=fa[v],w=W[v]^X[v&3];
G[u].push_back(v);
}
```
::::
每次复杂度就是 $O(n)$ 的。总体就是 $O(Tn)$。
## 代码
::::warning[未卡常代码]
沙子卡常题,差评差评差评差评差评差评差评差评。
```cpp
#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=2e5+10;
const int inf=1e9;
int n,m,fa[N],X[4],tp,W[N];
vector<int>G[N];
long long g[N],d[N];
int tot[(1<<17)+1],cnt[(1<<17)+1];
int ord[N];
void solve()
{
tp=0;
cin>>X[0]>>X[1]>>X[2]>>X[3]>>m;
for(int i=1;i<=n;i++)G[i].clear();
memset(tot,0,sizeof tot);
memset(cnt,0,sizeof cnt);
for(int i=2;i<=n;i++)tot[W[i]^X[i&3]]++;
for(int i=(1<<17);i>0;i--)cnt[i-1]=cnt[i]+tot[i];
for(int i=2;i<=n;i++)ord[++cnt[W[i]^X[i&3]]]=i;
for(int i=1;i<n;i++)
{
int v=ord[i];int u=fa[v],w=W[v]^X[v&3];
G[u].push_back(v);
}
for(int u=n;u>=1;u--)
{
d[u]=0;
for(auto v:G[u])
{
int w=W[v]^X[v&3];
if(!d[u])
{
d[u]=d[v]+w;
continue;
}
d[u]-=w;
if(d[u]<d[v])swap(d[u],d[v]);
g[++tp]=d[v];
d[u]+=w;
}
}
g[++tp]=d[1];
if(m==1)
{
cout<<d[1]<<"\n";
return ;
}
long long dp=0;
if(m<tp)nth_element(g+1,g+m+1,g+tp+1,greater<long long>());
for(int i=1;i<=min(m,tp);i++)dp+=g[i];
cout<<dp<<"\n";
}
signed main()
{
// freopen("mango.in","r",stdin);
// freopen("mango.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=2;i<=n;i++)cin>>fa[i];
for(int i=2;i<=n;i++)cin>>W[i];
int T;cin>>T;
while(T--)solve();
return 0;
}
```
::::