11.20
文章作者: cosf
可以先算期望,最后再乘 \((n-1)!\)。
对于 \(u, v\) 间的距离,我们可以把它转化成 \(d_u + d_v - 2d_{\operatorname{LCA}}(u, v)\),其中 \(d_u\) 是 \(u\) 到根的期望距离。
显然 \(d_u\) 是好算的:
前缀和搞搞就行了。
对于 \(u=v\) 的询问,显然答案为 \(0\)。下文不妨设 \(u \lt v\)。
然后对于 \(d_{\operatorname{LCA}(u, v)}\),对于每一个 \(x\),我们计算它是 \(\operatorname{LCA}\) 的概率,然后乘起来。记 \(f(x, u)\) 为 \(x\) 是 \(u\) 祖先的概率,\(g(x; u, v)\) 为 \(x\) 是 \(u, v\) 共同祖先的概率,\(h(x; u, v)\) 为 \(x\) 是 \(u, v\) 的 \(\operatorname{LCA}\) 的概率。
我们要的就是 \(\sum_{x=1}^ud_xh(x; u, v)\)。当 \(x = u\) 时显然 \(h(x; u, v) = f(u, v)\),我们单独讨论。
先看看怎么计算 \(f(u, v)\):
不难看出,当 \(u \lt v\) 时 \(f_{u, v} = \frac{1}{u}\),与 \(v\) 无关。
对于 \(g(x; u, v)\),当 \(u=v=x\) 时 \(g(x; u, v) = 1\)。否则当 \(u, v, x\) 有两个相等时 \(g(x; u, v) = f(x, v) = \frac{1}{x}\)。否则,结论是 \(g(x; u, v) = \frac{2}{x(x + 1)}\)。
这点可以按照 \(v\)(即较大的那个)进行归纳,这里就不写了。
对于 \(h(x; u, v)\),我们可以在 \(g(x; u, v)\) 的基础上进行一点容斥。如果 \(\operatorname{LCA}(u, v) = x'\) 且 \(x'\) 在 \(x\) 子树内,考虑 \(x'\) 到 \(x\) 路径上最靠近 \(x\) 的那个点 \(i\),可以得到:
非常的简单。后面的就随便处理一下 \(\frac{d_x}{x(x + 1)}\) 的前缀和即可,不要忘了前面的分类。
$ 99 \% $ 题解没有证明充分性。奇数码问题初探 这里有证明。
首先 \(n\) 数码问题有解的一个充要条件是 初始态与目标态中空格的距离奇偶性 和 把空格视为 \(0\) 并把网格按照写成序列后,初始态与目标态的逆序对数之和的奇偶性 相同,所谓奇数码问题大概是这个结论的弱化版本。
条件的必要性容易说明。至于充分性,可以构造证明,大概就是 \(n>3\) 时你可以通过摆好最外面一圈的数使得 \(n→n−1\);\(n=3\) 的时候直接搜一下就行了。注意到每次移动后,条件中两个东西的奇偶性都会发生变化,那它们是否相等这件事就不会改变,那就证完了。
P14509 luogu NOIP 模拟赛 T3 发现自己真的菜
P14508 luogu NOIP 模拟赛 T2 要反思为啥没想出来
P5256 非常规 KMP
P10634 hash 代替思考
P14517 新颖题
P6835 简单期望,基本上是模版转移。
P4253 很棒的树形dp
肯定是树形 DP,但是 DP 很难想。
考虑到数据范围和每次走另一个点实际上是和上一次停留在哪个点有关系。
设 \(f_{i,j}\) 表示走完 \(i\) 的子树(还没走完 \(i\)),最后停在 \(j\) 上。
发现 \(j\) 只于叶子节点有关,所以空间和时间复杂度都是对的。
\(f_{i,j}=\displaystyle\max_{x \in ls} (f_{ls,x} + dis(i,ls) \times a_i + dis(x,rs) \times a_{rs} ) +f_{rs,j},j \in rs\)
\(f_{i,j}=\displaystyle\max_{x \in rs} (f_{rs,x} + dis(i,rs) \times a_i + dis(x,ls) \times a_{ls} ) +f_{ls,j},j \in ls\)
但是题目没说一定从 \(1\) 开始。
\(f_{i,j}\) 肯定是从 \(i\) 开始走的,因为你要走回 \(i\) 这个节点上。
所以我们需要新定义一个 \(g_{i,j}\) 表示遍历完了 \(i\) 的整颗子树(包含 \(i\)),最终停在 \(j\)。
那么一开始 \(g_{i,j}=f_{i,j}\) 即出发点是 \(i\),
结束节点肯定也是叶子节点。
\(g_{i,j}=\displaystyle\min_{x \in ls}(a_i\times dis(x,ls)+g_{ls,x})+f_{rs,j}+a_{rs}\times dis(i,rs)\)
\(g_{i,j}=\displaystyle\min_{x \in rs}(a_i\times dis(x,rs)+g_{rs,x})+f_{ls,j}+a_{ls}\times dis(i,ls)\)
但是还是有一个特殊的,就是 \(n\) 为偶数的情况。
会有一个节点只有一个儿子,那我们好像就不会转移了。
但是我们其实可以直接在颗树下新建一个节点,其点权为极大值,边权为 \(0\)。
那么我们肯定会优先选择这个节点,然后再去另一个儿子,因为边权为 \(0\) 所以不会影响答案。
记得开动态数组。
#include<bits/stdc++.h>
#define ll __int128
#define pb push_back
using namespace std;
const int N=2e5+5;
const ll inf=1e24;
int n,fa[N],dep[N];
ll a[N],b[N],ans=inf;
vector<int> d[N],leaf[N];
vector<ll> f[N],g[N];
ll read(){
ll x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while('0'<=ch&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
void write(ll x){
if(x>9) write(x/10);
putchar(x%10+'0');
}
void dfs(int x){
if(!d[x].size()){
leaf[x].pb(x);
f[x].pb(0),g[x].pb(0);
return;
}
else{
// printf("%d\n",x);
int ls=d[x][0],rs=d[x][1];
dfs(ls),dfs(rs);
int sizl=leaf[ls].size(),sizr=leaf[rs].size();
leaf[x].resize(sizl+sizr),f[x].resize(sizl+sizr),g[x].resize(sizl+sizr);
ll mxfl=inf,mxfr=inf,mxgl=inf,mxgr=inf;
for(int i=0;i<sizl;i++){
mxfl=min(mxfl,a[ls]*(dep[ls]-dep[x])+f[ls][i]+a[rs]*(dep[leaf[ls][i]]+dep[rs]-2ll*dep[x]));
mxgl=min(mxgl,g[ls][i]+(dep[leaf[ls][i]]-dep[x])*a[x]);
}
for(int i=0;i<sizr;i++){
mxfr=min(mxfr,a[rs]*(dep[rs]-dep[x])+f[rs][i]+a[ls]*(dep[leaf[rs][i]]+dep[ls]-2ll*dep[x]));
mxgr=min(mxgr,g[rs][i]+(dep[leaf[rs][i]]-dep[x])*a[x]);
}
for(int i=0;i<sizl;i++){
leaf[x][i]=leaf[ls][i];
f[x][i]=mxfr+f[ls][i];
g[x][i]=min(f[x][i],mxgr+f[ls][i]+(dep[ls]-dep[x])*a[ls]);
}
for(int i=0;i<sizr;i++){
leaf[x][i+sizl]=leaf[rs][i];
f[x][i+sizl]=mxfl+f[rs][i];
g[x][i+sizl]=min(f[x][i+sizl],mxgl+f[rs][i]+(dep[rs]-dep[x])*a[rs]);
}
}
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=2;i<=n;i++) b[i]=read();
if(n%2==0) ++n,b[n]=0,a[n]=1e18;
for(int i=2;i<=n;i++) fa[i]=i/2,dep[i]=dep[fa[i]]+b[i],d[fa[i]].pb(i);
dfs(1);
// printf("!");
for(int i=0;i<g[1].size();i++) ans=min(ans,g[1][i]);
write(ans);
return 0;
}
P6076 二项式反演水题。
最小差值生成树,由于我不会 LCT,所以找到了两种不用 LCT 的办法。
首先是线段树分治。我们先二分答案,这样就知道每个边会在那些段时间中出现了。然后在途中维护每个时间的连通性就好了。复杂度比较劣,是 \(O(m\log m\log n\log V)\) 的,分别对应线段树分治拆时间,可撤销并查集,二分。
还有就是 整体二分。我们考虑设计状态 \(f_i\) 表示在第 \(i\) 条边及以后才能连边的最小的最大边权。可以发现这个函数是单调不降的,所以可以来整体二分。具体来说就是考虑 \(f_{mid}\) 的答案,然后二分下去 \([l,mid)\) 和 \((mid,r]\) 的答案,用可撤销并查集不然复杂度死了,时间复杂度是 \(O(m\log^2 m)\)。
浅谈一类处理状态转移依赖邻项的排列计数问题的 dp 策略:连续段 dp(线头 dp) - ChroneZ - 博客园
好牛呀这个做法,考虑限制比较松就先排序减少了放的限制,然后考虑先紧密排布的方案数,最后加上空位。
作者:EuphoricStar
先将所有磁铁按 \(r_i\) 从小到大排序,然后令 \(f_{i,j,k}\) 表示考虑了前 \(i\) 个磁铁,分为 \(j\) 组,占用的空位数为 \(k\) 的方案数。
那么我们有三个转移:
-
第 \(i\) 个磁铁单独成为了一个新组:\(f_{i,j,k} \to f_{i-1,j-1,k-1}\)。
-
第 \(i\) 个磁铁接在前面 \(j\) 个组的端点:\(f_{i,j,k} \to f_{i,j,k} + f_{i-1,j,k-a_i} \times j \times 2\)(\(k \ge a_i\))
-
第 \(i\) 个磁铁连接前面 \(j+1\) 个组的两个:\(f_{i,j,k} \to f_{i,j,k} + f_{i-1,j+1,k-2 \times a_i+1} \times j \times (j+1)\)(\(k \ge 2 \times a_i - 1\))。
最后我们得到了所有磁铁分为 \(1\) 组长度为 \(i\) 的方案数,那么它对答案的贡献为 \(f_{n,1,i} \times \binom{l-i+n}{n}\)(根据插板法易得)。
那么这题就做完了,时间复杂度 \(O(n^2 \times l)\)。
考虑连续段之后为了排除计算具体的贡献,你要知道未合并前为下凸函数。所以只需要关心新开段和确定一个段不会增长的时候的数的大小就好了,所以要多加一维表示有几个边界不能放东西了。容易漏掉一种一个点且有边界的转移。
但是这样到不了,原因是这个 \(V\) 一会加一会减的,值域太大了。考虑提前计算贡献来压缩值域,具体来说 \(a_i,a_{i+1}\) 会被计算的次数就是 \(a_{i+1}\) 被插入前的区间数,证明显然,然后就做完了,\(O(n^2L)\)。
下面是转移方程:
考虑连续段 dp 的几种转移即可。记 \(k' \leftarrow k+(a_{i+1}-a_{i}) \times (2j-d)\)。当我们插入 \(a_{i+1}\) 时,
-
作为一个新的连续段插入到不为边界的间隙中,即 \(f_{i+1,j+1,k',d} \leftarrow f_{i,j,k,d} \times(j+1-d)\)。
-
\((j \geq 2)\) 作为中间点合并两个连续段,即 \(f_{i+1,j-1,k',d} \leftarrow f_{i,j,k,d} \times(j-1)\)。-
-
\((j \geq 1)\) 作为一个新元素插入到某个连续段的非边界端点处,即 \(f_{i+1,j,k',d} \leftarrow f_{i,j,k,d} \times(2j-d)\)。
-
\((d< 2)\) 作为一个新的连续段作为边界插入,即 \(f_{i+1,j+1,k',d+1} \leftarrow f_{i,j,k,d} \times(2-d)\)。
-
\((d< 2,j \geq 1)\) 作为一个新元素作为边界端点插入到某个连续段,即 \(f_{i+1,j,k',d+1} \leftarrow f_{i,j,k,d} \times(2-d)\)。
答案显然为 \(\sum_{i=0}^{L}f_{n,1,i,2}\)。

浙公网安备 33010602011771号