【日常训练】【BZOJ】20210304_T2_树tree_树链剖分/dp/dp优化_Bzoj3644
题面
HZY 种了一棵树,如果你不知道树是啥,你可以直接去秒 T3 或者看百度给的详细解释: 树是具有木质树干及树枝的植物,可存活多年。一般将乔木称为树,主干,植株一,分枝距离地面较高,可以形成树冠,树有很多种。
为了方便 HZY 在树上走,她在树上的每个节点 i 都建了一个回旋加速喷气式阿姆斯特朗炮,可以把她发射到和点 i 距离不超过 a[i]的任意一个点上(两点间距离定义为它们之间边的数量),现在 HZY 想知道从一个点 x 到另一个点 y 至少要用几次回旋加速喷气式阿姆斯特朗炮(她不能通过回旋加速喷气式阿姆斯特朗
炮以外的方式移动)。(以及,必须在x和y之间的简单路径上移动)。
另外,有时候有些节点上的回旋加速喷气式阿姆斯特朗炮会进/退化。形式化的,有 m 个操作,每个形如 o,x,y 的形式,若 o 为 C,则表示把 a[x]改成 y;若 o 为 Q,则表示查询 x,y,查询保证 x!=y。
对于前 100%的数据,1≤n,m≤100000,1≤a[i]≤20。
题解
现场得分:60/10
现场题面里少了一句话导致这道题不太能做。
- 有一个简单的\(O(nm)\)的链上dp的做法。记\(f_i\)表示从开头到\(i\)最少要多少步。
- 我们考虑用树剖来维护链上的dp。
- 考虑为了合并信息,我们应该如何记录一个点上的东西。
- 我们发现对于一个表示的区间为\([l,r]\)的节点,我们可以记录三种东西:
- \(f_{i}\),表示从\(l+i-1\)开始,至少到\(r\)结束(可以超出去),我们最少要多少步。
- \(g_{i}\),表示从\(l+i-1\)开始,至少到\(r\)结束(可以超出去),在\(f_i\)最小的情况下最多超出去多少。
- 我们发现可能会有这一段亏一步来拿到一个20的长步子,但是又最多只会亏一步。所以我们再记录\([l,r]\)中的\(\max i+a_i\),其中\(a_i\)表示在\(i\)上可以跨多少步。
- 直接转移。
- 修改点值的时候, 一次是\(20\log n\)的。
- 注意回答询问的时候,我们只需要维护\(f_1,g_1\),所以回答的时候是\(\log^2 n\)的。
- 复杂度\(O(na\log n+n\log^2n)\)。
代码
#include<bits/stdc++.h>
#define LL long long
#define MAXN 101000
using namespace std;
template<typename T> void Read(T &cn)
{
char c; int sig = 1;
while(!isdigit(c = getchar())) if(c == '-') sig = 0;
if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10+c-48; }
else {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; }
}
template<typename T> void Write(T cn)
{
int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
if(cn < 0 || cx < 0) {putchar('-'); cn = 0-cn; cx = 0-cx; }
while(cn)cm = cm*10+cn%10,cn/=10,wei++;
while(wei--)putchar(cm%10+48),cm/=10;
putchar(cx+48);
}
template<typename T> void WriteL(T cn) {Write(cn); puts(""); }
template<typename T> void WriteS(T cn) {Write(cn); putchar(' '); }
template<typename T> void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template<typename T> void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
struct Dp{
int yuan, len;
int f[21], g[21];
void outit()
{
printf("yuan = %d len = %d ",yuan,len);
for(int i = 1;i<=2;i++) printf("f[%d] = %d g[%d] = %d ",i,f[i],i,g[i]);
puts("");
}
void pre(int cn) {len = cn; for(int i = 0;i<=20;i++) f[i] = MAXN+1, g[i] = 0, f[1] = 0; g[1] = 1; yuan = 0; }
void set(int cn)
{
yuan = cn;
for(int i = 1;i<=20;i++) f[i] = 0, g[i] = i-1;
f[0] = MAXN; g[0] = 0;
}
};
void update(Dp &cn, Dp ls, Dp rs, int cm)
{
// printf("in update : cm = %d\n",cm);
// printf(" ls = "); ls.outit();
// printf(" rs = "); rs.outit();
cn.yuan = max(rs.yuan, ls.yuan-rs.len); cn.len = ls.len + rs.len;
cn.f[0] = MAXN+1, cn.g[0] = 0;
for(int i = 1;i<=cm;i++)
{
if(i >= cn.len) cn.f[i] = 0, cn.g[i] = i-cn.len;
cn.f[i] = ls.f[i] + rs.f[ls.g[i]], cn.g[i] = rs.g[ls.g[i]];
int lin = ls.f[i]+1 + rs.f[ls.yuan], lin2 = rs.g[ls.yuan];
if(lin < cn.f[i] || (lin == cn.f[i] && lin2 > cn.g[i])) cn.f[i] = lin, cn.g[i] = lin2;
}
// printf(" cn = "); cn.outit();
}
struct Seg{
Dp t[MAXN*4+1];
int n;
void build(int cn, int l, int r, int a[])
{
t[cn].pre(r-l+1); if(l == r) {t[cn].set(a[l]); return; }
int zh = (l+r)>>1; build(cn<<1,l,zh,a); build((cn<<1)|1,zh+1,r,a);
update(t[cn], t[cn<<1], t[(cn<<1)|1], 20);
}
void gai(int cn, int cm, int cx, int l, int r)
{
if(l == r) {t[cn].set(cx); return; }
int zh = (l+r)>>1;
if(cm <= zh) gai(cn<<1,cm,cx,l,zh);
else gai((cn<<1)|1,cm,cx,zh+1,r);
update(t[cn], t[cn<<1], t[(cn<<1)|1], 20);
}
void qiu(int cn, int cl, int cr, int l, int r, Dp &ans)
{
if(cl <= l && r <= cr) {
// printf("re qiu : cn = %d l = %d r = %d\n",cn,l,r);
update(ans, ans, t[cn], 1); return;
}
int zh = (l+r)>>1;
if(cl <= zh) qiu(cn<<1, cl, cr, l, zh, ans);
if(cr > zh) qiu((cn<<1)|1, cl,cr,zh+1,r,ans);
}
void build(int cn, int a[]) {n = cn; build(1,1,n,a); }
void gai(int cn, int cm) {gai(1,cn,cm,1,n); }
void qiu(int cl, int cr, Dp &ans) {qiu(1,cl,cr,1,n,ans); }
}T1, T2;
struct qwe{
int a,b,ne;
void mk(int cn, int cm, int cx) {a = cn; b = cm; ne = cx; }
};
struct Qujian{
int l, r;
void mk(int cn, int cm) {l = cn; r = cm; }
};
qwe a[MAXN*2+1];
int alen;
int head[MAXN+1];
int n, q;
int zhi[MAXN+1], zhi2[MAXN+1];
int dfn[MAXN+1], lie[MAXN+1], shi;
int fa[MAXN+1], size[MAXN+1], shen[MAXN+1];
int top[MAXN+1], zhong[MAXN+1];
Qujian zhan1[MAXN+1], zhan2[MAXN+1];
int zlen1, zlen2;
void lian(int cn, int cm) {a[++alen].mk(cn,cm,head[cn]); head[cn] = alen; }
void dfs1(int cn)
{
size[cn] = 1; zhong[cn] = 0;
for(int i = head[cn];i;i = a[i].ne)
{
int y = a[i].b;
if(y == fa[cn]) continue;
fa[y] = cn; shen[y] = shen[cn]+1;
dfs1(y); size[cn] += size[y];
if(size[zhong[cn]] < size[y]) zhong[cn] = y;
}
}
void dfs2(int cn)
{
lie[dfn[cn] = ++shi] = cn;
if(zhong[cn]) {
top[zhong[cn]] = top[cn];
dfs2(zhong[cn]);
}
for(int i = head[cn];i;i = a[i].ne)
{
int y = a[i].b;
if(y == fa[cn] || y == zhong[cn]) continue;
top[y] = y; dfs2(y);
}
}
int suan(int cn, int cm)
{
zlen1 = zlen2 = 0;
while(top[cn] != top[cm])
{
if(shen[top[cn]] < shen[top[cm]]) zhan2[++zlen2].mk(dfn[top[cm]], dfn[cm]), cm = fa[top[cm]];
else zhan1[++zlen1].mk(dfn[top[cn]], dfn[cn]), cn = fa[top[cn]];
}
if(shen[cn] < shen[cm]) zhan2[++zlen2].mk(dfn[cn], dfn[cm]);
else zhan1[++zlen1].mk(dfn[cm], dfn[cn]);
Dp ans; ans.pre(0);
for(int i = 1;i<=zlen1;i++) T2.qiu(n-zhan1[i].r+1, n-zhan1[i].l+1, ans);
for(int i = zlen2;i>=1;i--) T1.qiu(zhan2[i].l, zhan2[i].r, ans);
int guo = ans.f[1];
for(int i = 2;i<=20;i++) Min(guo, ans.f[i]);
return guo;
}
int main()
{
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
Read(n);
for(int i = 1;i<=n;i++) Read(zhi[i]);
alen = 0; memset(head,0,sizeof(head));
for(int i = 2;i<=n;i++) {int bx,by; Read(bx); Read(by); lian(bx,by); lian(by,bx); }
shi = 0; shen[1] = 1; shen[0] = 0; fa[1] = fa[0] = 0; top[1] = 1; size[0] = 0;
dfs1(1); dfs2(1);
for(int i = 1;i<=n;i++) zhi2[i] = zhi[lie[i]];
T1.build(n, zhi2);
for(int i = 1;i<=n/2;i++) swap(zhi2[i], zhi2[n-i+1]);
T2.build(n, zhi2);
Read(q);
for(int i = 1;i<=q;i++)
{
char bc; int bx, by;
while(!isalpha(bc = getchar())); Read(bx); Read(by);
if(bc == 'C') {zhi[bx] = by; T1.gai(dfn[bx], by); T2.gai(n-dfn[bx]+1, by); }
if(bc == 'Q') WriteL(suan(bx,by));
}
return 0;
}