【题目描述】
描述
多年前 Lyra 在 Evan 的壁炉里发现了一棵树,这棵树非常奇特,它贴着壁炉的内壁生长,汲取火焰中的魔法元素给自己提供养分,善于记忆的 Lyra 记下了当时树的形态。
当年年幼无知的 Lyra 还不知道 Evan 家这棵树具有灵性,继续生长下去有着无尽的潜力和价值。如今 Lyra 又站在 Evan 的壁炉旁,发现 Evan 家的这棵树已经长了不少,作为 Lyra 的好朋友, Evan 告诉 Lyra,这棵灵树生长需要 Evan 投入魔法石到火焰中,而它生长过程也十分奇特:
这棵树可以抽象成一棵带边权的有根树,由于生长在墙壁上,每个节点的所有孩子节点之间是有相对顺序的(可以假设为从左至右),此外树还有两个属性 c1,c2,树的生长包含四种变化:
- 生长: 对于树的一个节点 x,考虑其孩⼦节点 y1,y2,⋯,ym,从 x 上长出一个新节点 z 插入到其孩子中第 k 个位置,于是 x 的孩子节点序列变成了 y1,y2,⋯,yk−1,z,yk,yk+1⋯,ym。 将 <x,z> 的权值设为 w 的消耗为 c1w 单位魔法石。
- 伸展: 对于树的一个节点 x,考虑其孩子节点 y1,y2,⋯,ym,选取一个区间 [l,r](1≤l≤r≤m),从 x 上长出一个新节点 z 作为 yl,yl+1,⋯,yr 的父亲。此时 x 的孩子序列为y1,y2,⋯,yl−1,z,yr+1,⋯,ym, z 的孩子序列为 yl,yl+1,⋯,yr。并且对于 l≤i≤r,边 <z,yi>就是原树上的边 <x,yi> 。将 <x,z> 的权值设为 w 的消耗为 c1w 单位魔法石。
- 收缩: 对于树的一个节点 x,考虑其孩子节点 y1,y2,⋯,ym,选取其一个孩子节点 yk,令其孩子序列为 z1,z2,⋯,zp,将 <x,yk> 缩掉(即去掉 yk,并将 yk 的所有孩子保持顺序接到yk 原来在 x 孩子中的位置)。此时 x 的孩子序列为 y1,y2,⋯,yk−1,z1,z2,⋯,zp,yk+1,⋯,ym。并且对于 1≤i≤p,边 <x,zi> 就是原树上的边 <yk,zi>。令 w 等于原树上 <x,yk> 的权值,则此操作需要消耗 c1w 单位魔法石。
- 转化: 对于树上一个节点 x 和其某个孩子 y,保持 y 在 x 的孩子序列中位置不变,把<x,y> 的权值从 w1(现有权值)修改为 w2,需要消耗 c2∣w1−w2∣ 单位的魔法石。
注意:第四种操作使用的魔法与前三种操作有所不同,所以由操作“生长”和“伸展”生成的边无法继续参与“转化”,同时“转化”过的边无法被“收缩”。
Lyra 知道魔法石是一种贵重的资源,很少有人能这样烧着玩,为了弄清楚 Evan 究竟有多富有,她想通过树的形态变化来确定这些年 Evan 至少花费了多少魔法石。
现在给出 Lyra 小时候树的形态与边权,和现在树的形态与边权,求由如上四个操作把树做如此变化的最小消耗。
两棵树被认为是相同当且仅当存在一个编号的映射,使得在该映射下一棵树与另一棵树的边,每个节点的孩子顺序,以及对应边权都完全相同,且根保持不变。
n 1 < = 50 , n 2 < = 2000 n_1<=50,n_2<=2000 n1<=50,n2<=2000
【思路】
毒瘤题。注意到生长伸展和收缩互为逆操作,我们可以把在一棵树上生长伸展和收缩,等价地变为在两棵树上同时收缩。暴力即
2
n
2^n
2n枚举每条边是否收缩。考虑括号序列,那么收缩意味着删除一对括号,转化意味着改变一对括号的权值,那么问题转化成了给定两个括号序列,问使这两个括号序列匹配的最小代价。定义
f
l
,
r
,
x
,
y
f_{l,r,x,y}
fl,r,x,y表示第一棵树的括号序列的区间[l,r]和第二颗树的区间[x,y]匹配的最小代价。一对括号没有被删除当且仅当左右括号都没有被删除。所以转移时我们可以从左到右考虑每个左括号,跳过右括号(右括号所在括号对已被删除)。转移时考虑三种情况:1.删除第一棵树的左括号。2.删除第二颗树的左括号。3.通过转化使两个左括号匹配。时间复杂度
O
(
n
1
2
n
2
2
)
O(n_1^2n_2^2)
O(n12n22)。考虑优化,对于一段正在考虑的区间,括号序列形如:
(
.
.
.
)
(
.
.
.
)
.
.
.
(
.
.
.
)
(
.
.
.
)
(...)(...)...(...)(...)
(...)(...)...(...)(...)
为了尽量避开第二颗树的重儿子,我们可以比较最左端括号和最右端括号的大小,只考虑删除小一点的那个括号的左(右)括号。转移时跳过另一半已被删除的左右括号即可。可以证明,第二颗树的转移时间复杂度为
O
(
∑
s
i
z
e
轻
儿
子
)
=
O
(
n
2
l
o
g
n
2
)
O(\sum size_{轻儿子})=O(n_2logn_2)
O(∑size轻儿子)=O(n2logn2),第一棵树转移时间复杂度
O
(
n
1
2
)
O(n_1^2)
O(n12)。总时间复杂度为
O
(
n
1
2
n
2
l
o
g
n
2
)
O(n_1^2n_2logn_2)
O(n12n2logn2)。
代码:(递归写法,常数巨大)
由于std丑到没办法看,代码参考了网上一位大佬的写法
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=1e5+5,mod=19260817;
inline int red(){
int data=0;bool w=0;char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return w?-data:data;
}typedef long long ll;
int n[2],a,b;ll c1,c2;
ll g[mod],vis[mod];
struct node{int u,v,w;}e[2][N];
int f[2][N],nxp[2][N],cnt[2],d[2][N],st[2][N],ed[2][N],tot[2];
const ll H1=103*103,H2=103*103*4003;
inline ll hash(const int&l,const int&r,const int&x,const int&y){return (ll)r+103ll*l+H1*x+H2*y;}
inline int find(int l,int r,int x,int y){
ll p=hash(l,r,x,y);int pos=p%mod;
while(vis[pos]&&vis[pos]!=p)((++pos)==mod)&&(pos=0);
if(!vis[pos])vis[pos]=p;
return pos;
}
inline void add(const int&u,const int&v,const int&w,const int&t){e[t][++cnt[t]]=(node){u,v,w};nxp[t][cnt[t]]=f[t][u];f[t][u]=cnt[t];}
void dfs(int u,const int&t){
for(int re i=f[t][u];i;i=nxp[t][i]){
int v=e[t][i].v;
d[t][++tot[t]]=i;st[t][i]=tot[t];
dfs(v,t);
d[t][++tot[t]]=i;ed[t][i]=tot[t];
}
}
void init(const int&t){
n[t]=red();
for(int re i=1;i<=n[t];i++)
for(int re j=red();j;--j)
a=red(),b=red(),add(i,a,b,t);
}
int*d1=d[0],*st1=st[0],*ed1=ed[0];
int*d2=d[1],*st2=st[1],*ed2=ed[1];
node*e1=e[0],*e2=e[1];
inline void cmin(ll&x,const ll&y){(x>y)&&(x=y);}
ll dp(int l,int r,int x,int y){
while(l<=r&&(ed1[d1[l]]>r||ed1[d1[l]]==l))++l;
while(l<=r&&(st1[d1[r]]<l||st1[d1[r]]==r))--r;
while(x<=y&&(ed2[d2[x]]>y||ed2[d2[x]]==x))++x;
while(x<=y&&(st2[d2[y]]<x||st2[d2[y]]==y))--y;
ll pos=find(l,r,x,y);
if(g[pos])return g[pos]-1;
if(l>r||x>y){
ll sum=0;
for(int re i=l;i<=r;i++)if(st1[d1[i]]==i&&ed1[d1[i]]<=r)sum+=e1[d1[i]].w;
for(int re i=x;i<=y;i++)if(st2[d2[i]]==i&&ed2[d2[i]]<=y)sum+=e2[d2[i]].w;
sum*=c1;
return g[pos]=sum+1,sum;
}
if(ed2[d2[x]]-st2[d2[x]]<=ed2[d2[y]]-st2[d2[y]]){
ll sum=dp(l+1,ed1[d1[l]]-1,x+1,ed2[d2[x]]-1)+dp(ed1[d1[l]]+1,r,ed2[d2[x]]+1,y)+c2*abs(e1[d1[l]].w-e2[d2[x]].w);
cmin(sum,dp(l+1,r,x,y)+c1*e1[d1[l]].w);
cmin(sum,dp(l,r,x+1,y)+c1*e2[d2[x]].w);
return g[pos]=sum+1,sum;
}else{
ll sum=dp(l,st1[d1[r]]-1,x,st2[d2[y]]-1)+dp(st1[d1[r]]+1,r-1,st2[d2[y]]+1,y-1)+c2*abs(e1[d1[r]].w-e2[d2[y]].w);
cmin(sum,dp(l,r-1,x,y)+c1*e1[d1[r]].w);
cmin(sum,dp(l,r,x,y-1)+c1*e2[d2[y]].w);
return g[pos]=sum+1,sum;
}
}
signed main(){
c1=red();c2=red();
init(0);init(1);dfs(1,0);dfs(1,1);
std::cout<<dp(1,tot[0],1,tot[1])<<"\n";
}