移花接木
题目概述
题解
首先,由于我们的
∣
T
i
∣
|T_i|
∣Ti∣可能达到
1
0
18
10^{18}
1018,所以显然我们不可能对于所有的点都去,显然,我们是只会维护一部分关键点的。
由于在树上我们有结论,到一个点最远的点中必然有一个点是直径的两端点之一。
那么我们只需要找到这两个直径的端点,算这两个点到直径中点两侧别的点的距离和就行。
如何在合并两棵树时,求新树的直径?
从上面的结论可以发现,我们新形成的直径端点一定是原来直径的端点。
那么我们可以将原来的直径自由组合,得到新的直径。
显然,在这种情况下,在某一层我们会真正参与递归的点必然是从后面的层传递下来的或者本层的分割点与直径端点。
这也就说明我们的总点数是
O
(
m
)
O\left(m\right)
O(m)的。
那么我们这部分的时间复杂度应该是
O
(
m
3
)
O\left(m^3\right)
O(m3)。
在求出直径后,我们又该怎么计算我们的答案呢?
我们不妨假设我们我们的直径长度为
L
L
L,先考虑
L
L
L为奇数的情况。
显然可以发现,右侧的点都是到
S
S
S最优,左侧的点都是到
T
T
T最优。
我们可以尝试从
S
S
S走到
v
v
v,统计
v
v
v子树内所有点到
v
v
v的路径长度和,从
T
T
T走到
u
u
u,统计
u
u
u子树内所有点到
u
u
u的路径长度和。
相当于让所有点走到了自己对应的
u
/
v
u/v
u/v,之后就再让它们走到
T
/
S
T/S
T/S就行了,显然之后的路径长度是
⌈
L
2
⌉
\lceil\frac{L}{2}\rceil
⌈2L⌉。
那么我们需要计算的就是在树
i
i
i内以
r
t
rt
rt为根,向点
x
x
x的方向走
d
d
d步所到点的子树内所有点的距离和,我们记为
F
i
(
r
t
,
x
,
d
)
F_i(rt,x,d)
Fi(rt,x,d)。
显然其转移也可以像我们上面一样记忆化递归求解,同样它的状态数也是比较少的,但是转移要看
r
t
,
x
rt,x
rt,x与割点的位置关系来转移。
不同的位置关系有
4
4
4种:
- u − v u-v u−v能够将 S − T S-T S−T分隔开,显然此时只需要考虑我们走 d d d步关于 u − v u-v u−v的位置就能继续递归下去。
- u − v u-v u−v在 T T T的子树内,那么我们分割后的另一子树一定会被统计到 F F F内去,继续递归。
- u − v u-v u−v在 S S S的另一子树内,必然不可能被统计入答案,继续递归。
- u − v u-v u−v从 S − T S-T S−T的路径分叉出去,这种情况要依据 d d d的大小,判断该子树能否统计入答案,继续递归。
至于如何判断属于哪种情况,我们可以通过
d
i
s
(
s
,
u
)
,
d
i
s
(
s
,
t
)
,
d
i
s
(
t
,
u
)
dis(s,u),dis(s,t),dis(t,u)
dis(s,u),dis(s,t),dis(t,u)三个量互相之间的大小关系判断。
这三个量可以用我们上面求直径的方式递归求出。
然后,我们就可以记忆化
d
p
dp
dp求出我们的答案了。
于是当
L
L
L为奇数的答案为:
A
n
s
i
=
F
i
(
S
,
T
,
⌈
L
2
⌉
)
+
F
i
(
T
,
S
,
⌈
L
2
⌉
)
+
⌈
L
2
⌉
∣
T
i
∣
Ans_i=F_i(S,T,\lceil\frac{L}{2}\rceil)+F_i(T,S,\lceil\frac{L}{2}\rceil)+\lceil\frac{L}{2}\rceil|T_i|
Ansi=Fi(S,T,⌈2L⌉)+Fi(T,S,⌈2L⌉)+⌈2L⌉∣Ti∣
那么之后我们考虑我们
L
L
L为偶数的又是怎样的一个情况。
如图:
其中,我们
u
u
u的子树与
v
v
v的子树是仍然可以按我们上面的求出,但此时我们
u
,
v
u,v
u,v中间点就不能这样了,因为它之后需要走的路径比
u
,
v
u,v
u,v小
1
1
1,也就是说我们最后不能像
⌈
L
i
2
⌉
∣
T
i
∣
\lceil\frac{L_i}{2}\rceil|T_i|
⌈2Li⌉∣Ti∣这样直接加起来,我们还需要计算一下两边子树内的节点数。
我们不妨直接把中间的点归入一侧,只是两侧的距离不一样罢了。
我们记
G
i
(
r
t
,
x
,
d
)
G_i(rt,x,d)
Gi(rt,x,d)表示在第
i
i
i棵树内,以点
r
t
rt
rt为根,向点
x
x
x走
d
d
d步,到的点的子树内的节点数。
这依然可以像我们上面求
F
F
F一样,递归求出,甚至还要更加简单。
可以得到,
L
L
L为偶数时,我们的答案为:
A
n
s
i
=
F
i
(
S
,
T
,
⌊
L
2
⌋
)
+
F
i
(
T
,
S
,
⌊
L
2
⌋
+
1
)
+
⌊
L
2
⌋
∣
T
i
∣
+
G
i
(
T
,
S
,
L
2
+
1
)
Ans_i=F_i(S,T,\lfloor\frac{L}{2}\rfloor)+F_i(T,S,\lfloor\frac{L}{2}\rfloor+1)+\lfloor\frac{L}{2}\rfloor |T_i|+G_i(T,S,\frac{L}{2}+1)
Ansi=Fi(S,T,⌊2L⌋)+Fi(T,S,⌊2L⌋+1)+⌊2L⌋∣Ti∣+Gi(T,S,2L+1)具体做法与上面的一样。
另外要注意的一点是,由于我们的
F
i
(
x
,
y
,
0
)
F_i(x,y,0)
Fi(x,y,0)这样的可能会多种
y
y
y不一样的情况,但实际上算的都是整棵树的情况,对于这种我们不如直接记忆化
F
i
(
x
,
x
,
0
)
F_i(x,x,0)
Fi(x,x,0),这样方便点。
当然,这种情况由于要覆盖整棵树,与上面的四种情况实际上是不太一样的,递归时也要单独讨论。
时间复杂度 O ( m 3 ) O\left(m^3\right) O(m3),但由于我用了 m a p map map来维护点新的编号实际上是 O ( m 3 log m ) O\left(m^3\log m\right) O(m3logm)的,或许 u n o r d e r e d _ m a p \rm unordered\_ map unordered_map会快一点。
源码
好像是跑得最慢的,不过也不卡常,不到时限的一半。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
#define MAXN 100010
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const int mod=1e5+7;
const int mo=998244353;
const int inv2=499122177;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int m,idp[155],F[155][MAXN],G[155][MAXN];
LL f[155],dis[155][305][305];pii p[155];
struct tann{
LL u,v,d;tann(){u=v=d=0;}
tann(LL U,LL V,LL D){u=U;v=V;d=D;}
bool operator != (const tann &rhs){return u!=rhs.u||v!=rhs.v||d!=rhs.d;}
int getpos(){return (u+v+d)%mod;}
};
struct ming{int a,b;LL u,v;}s[155];
map<LL,int>mp[155];
int ask(int x,LL y){
if(mp[x].find(y)==mp[x].end())mp[x][y]=++idp[x];
return mp[x][y];
}
struct HashMap{
int head[MAXN],tot,nxt[MAXN];tann val[MAXN];
int query(tann ai){
int pos=ai.getpos(),now=head[pos];
while(now&&val[now]!=ai)now=nxt[now];
if(!now)now=++tot,val[now]=ai,nxt[now]=head[pos],head[pos]=now;
return now;
}
}Mp[155];
LL getDis(int id,LL x,LL y){
if(x>y)swap(x,y);int u=ask(id,x),v=ask(id,y);
if(~dis[id][u][v])return dis[id][u][v];
if(x<=f[s[id].a]&&y<=f[s[id].a])return dis[id][u][v]=getDis(s[id].a,x,y);
if(x>f[s[id].a]&&y>f[s[id].a])
return dis[id][u][v]=getDis(s[id].b,x-f[s[id].a],y-f[s[id].a]);
dis[id][u][v]=getDis(s[id].a,x,s[id].u)+getDis(s[id].b,y-f[s[id].a],s[id].v)+1LL;
return dis[id][u][v];
}
int getG(int id,LL x,LL y,LL d){
if(!d)y=x;int S=Mp[id].query(tann(x,y,d));
if(~G[id][S])return G[id][S];
LL tmp1=getDis(id,x,y),X=x,Y=y,D=d;
LL u=s[id].u,v=s[id].v;const int a=s[id].a,b=s[id].b;
if(x<=f[a]){
LL tmp2=getDis(id,x,u),tmp3=getDis(id,y,u);
if(X==Y)G[id][S]=f[id];
else if(y>f[a]){
y-=f[a];
if(tmp2>=d)G[id][S]=add(getG(a,x,u,d),f[b]%mo,mo);
else G[id][S]=getG(b,v,y,d-tmp2-1LL);
}
else if(tmp1+tmp3==tmp2)
G[id][S]=add(getG(a,x,y,d),f[b]%mo,mo);
else if(tmp1+tmp2==tmp3)G[id][S]=getG(a,x,y,d);
else{
LL t=(tmp2+tmp3-tmp1)/2LL;
if(d+t>tmp2)G[id][S]=getG(a,x,y,d);
else G[id][S]=add(getG(a,x,y,d),f[b]%mo,mo);
}
}
else{
LL tmp2=getDis(id,x,v+f[a]),tmp3=getDis(id,y,v+f[a]);x-=f[a];
if(X==Y)G[id][S]=f[id];
else if(y<=f[a]){
if(tmp2>=d)G[id][S]=add(getG(b,x,v,d),f[a]%mo,mo);
else G[id][S]=getG(a,u,y,d-tmp2-1LL);
}
else if(tmp1+tmp3==tmp2)
y-=f[a],G[id][S]=add(getG(b,x,y,d),f[a]%mo,mo);
else if(tmp1+tmp2==tmp3)y-=f[a],G[id][S]=getG(b,x,y,d);
else{
LL t=(tmp2+tmp3-tmp1)/2LL;y-=f[a];
if(d+t>tmp2)G[id][S]=getG(b,x,y,d);
else G[id][S]=add(getG(b,x,y,d),f[a]%mo,mo);
}
}
return G[id][S];
}
int getF(int id,LL x,LL y,LL d){
if(!d)y=x;int S=Mp[id].query(tann(x,y,d));
if(~F[id][S])return F[id][S];
LL tmp1=getDis(id,x,y);int res=0;
LL u=s[id].u,v=s[id].v;const int a=s[id].a,b=s[id].b;
LL X=x,Y=y,D=d;
if(x<=f[a]){
LL tmp2=getDis(id,x,u),tmp3=getDis(id,y,u);
if(X==Y){
res=add(getF(a,x,x,0),1ll*(tmp2+1LL)*(f[b]%mo)%mo,mo);
Add(res,getF(b,v,v,0),mo);F[id][S]=res;
}
else if(y>f[a]){
y-=f[a];
if(tmp2>=d)Add(res,getF(a,x,u,d),mo),
Add(res,getF(b,v,y,0),mo),
Add(res,1ll*(tmp2+1LL-d)%mo*(f[b]%mo)%mo,mo);
else res=getF(b,v,y,d-tmp2-1LL);
F[id][S]=res;
}
else if(tmp1+tmp3==tmp2){
Add(res,getF(a,x,y,d),mo);
Add(res,1ll*(tmp2+1LL-d)%mo*(f[b]%mo)%mo,mo);
Add(res,getF(b,v,v,0),mo);
F[id][S]=res;
}
else if(tmp1+tmp2==tmp3)F[id][S]=getF(a,x,y,d);
else{
LL t=(tmp2+tmp3-tmp1)/2LL;
if(d+t>tmp2)res=getF(a,x,y,d);
else res=add(getF(a,x,y,d),1ll*(tmp2+1LL-d)%mo*(f[b]%mo)%mo,mo),
Add(res,getF(b,v,v,0),mo);
F[id][S]=res;
}
}
else{
LL tmp2=getDis(id,x,v+f[a]),tmp3=getDis(id,y,v+f[a]);x-=f[a];
if(X==Y){
res=add(getF(b,x,x,0),1ll*(tmp2+1LL)*(f[a]%mo)%mo,mo);
Add(res,getF(a,u,u,0),mo);F[id][S]=res;
}
else if(y<=f[a]){
if(tmp2>=d)Add(res,getF(b,x,v,d),mo),
Add(res,getF(a,u,y,0),mo),
Add(res,1ll*(tmp2+1LL-d)%mo*(f[a]%mo)%mo,mo);
else res=getF(a,u,y,d-tmp2-1LL);
F[id][S]=res;
}
else if(tmp1+tmp3==tmp2){
y-=f[a];Add(res,getF(b,x,y,d),mo);
Add(res,1ll*(tmp2+1LL-d)%mo*(f[a]%mo)%mo,mo);
Add(res,getF(a,u,u,0),mo);
F[id][S]=res;
}
else if(tmp1+tmp2==tmp3)y-=f[a],F[id][S]=getF(b,x,y,d);
else{
LL t=(tmp2+tmp3-tmp1)/2LL;y-=f[a];
if(d+t>tmp2)res=getF(b,x,y,d);
else res=add(getF(b,x,y,d),1ll*(tmp2+1LL-d)%mo*(f[a]%mo)%mo,mo),
Add(res,getF(a,u,u,0),mo);
F[id][S]=res;
}
}
return F[id][S];
}
int main(){
freopen("link.in","r",stdin);
freopen("link.out","w",stdout);
read(m);f[0]=1;bool flag=0;
for(int i=1;i<=m;i++){
read(s[i].a),read(s[i].b),read(s[i].u),read(s[i].v);
f[i]=f[s[i].a]+f[s[i].b];
}
memset(dis,-1,sizeof(dis));
memset(F,-1,sizeof(F));
memset(G,-1,sizeof(G));
p[0]=mkpr(1,1);int x=ask(0,1);dis[0][x][x]=0;
x=Mp[0].query(tann(1,1,0));F[0][x]=0;G[0][x]=1;
for(int i=1;i<=m;i++){
LL ax=p[s[i].a].fir,ay=p[s[i].a].sec;LL tmp=0;
LL bx=p[s[i].b].fir+f[s[i].a],by=p[s[i].b].sec+f[s[i].a];
LL tmp1=getDis(i,ax,ay),tmp2=getDis(i,bx,by);tmp=max(tmp,max(tmp1,tmp2));
LL tmp3=getDis(i,ax,bx),tmp4=getDis(i,ax,by);tmp=max(tmp,max(tmp3,tmp4));
LL tmp5=getDis(i,ay,bx),tmp6=getDis(i,ay,by);tmp=max(tmp,max(tmp5,tmp6));
if(tmp==tmp1)p[i]=mkpr(ax,ay);
else if(tmp==tmp2)p[i]=mkpr(bx,by);
else if(tmp==tmp3)p[i]=mkpr(ax,bx);
else if(tmp==tmp4)p[i]=mkpr(ax,by);
else if(tmp==tmp5)p[i]=mkpr(ay,bx);
else if(tmp==tmp6)p[i]=mkpr(ay,by);
}
for(int i=1;i<=m;i++){
LL tmp=getDis(i,p[i].fir,p[i].sec);int ans=0;
if(tmp&1LL){
Add(ans,getF(i,p[i].fir,p[i].sec,(tmp+1LL)/2LL),mo);
Add(ans,getF(i,p[i].sec,p[i].fir,(tmp+1LL)/2LL),mo);
Add(ans,1ll*(tmp+1LL)/2LL%mo*(f[i]%mo)%mo,mo);
}
else{
Add(ans,getF(i,p[i].fir,p[i].sec,tmp/2LL),mo);
Add(ans,getF(i,p[i].sec,p[i].fir,tmp/2LL+1),mo);
Add(ans,1ll*(tmp/2LL+1LL)%mo*(f[i]%mo)%mo,mo);
Add(ans,mo-getG(i,p[i].fir,p[i].sec,tmp/2LL),mo);
}
printf("%d\n",ans);
}
return 0;
}
谢谢!!!