【括号序列】【dp】Gosling

【题目描述】
描述
多年前 Lyra 在 Evan 的壁炉里发现了一棵树,这棵树非常奇特,它贴着壁炉的内壁生长,汲取火焰中的魔法元素给自己提供养分,善于记忆的 Lyra 记下了当时树的形态。
当年年幼无知的 Lyra 还不知道 Evan 家这棵树具有灵性,继续生长下去有着无尽的潜力和价值。如今 Lyra 又站在 Evan 的壁炉旁,发现 Evan 家的这棵树已经长了不少,作为 Lyra 的好朋友, Evan 告诉 Lyra,这棵灵树生长需要 Evan 投入魔法石到火焰中,而它生长过程也十分奇特:
这棵树可以抽象成一棵带边权的有根树,由于生长在墙壁上,每个节点的所有孩子节点之间是有相对顺序的(可以假设为从左至右),此外树还有两个属性 c1,c2,树的生长包含四种变化:

  1. 生长: 对于树的一个节点 x,考虑其孩⼦节点 y1,y2,⋯,ym,从 x 上长出一个新节点 z 插入到其孩子中第 k 个位置,于是 x 的孩子节点序列变成了 y1,y2,⋯,yk−1,z,yk,yk+1⋯,ym。 将 <x,z> 的权值设为 w 的消耗为 c1w 单位魔法石。
  2. 伸展: 对于树的一个节点 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 单位魔法石。
  3. 收缩: 对于树的一个节点 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 单位魔法石。
  4. 转化: 对于树上一个节点 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

【思路】

毒瘤题。注意到生长伸展和收缩互为逆操作,我们可以把在一棵树上生长伸展和收缩,等价地变为在两棵树上同时收缩。暴力即 2 n 2^n 枚举每条边是否收缩。考虑括号序列,那么收缩意味着删除一对括号,转化意味着改变一对括号的权值,那么问题转化成了给定两个括号序列,问使这两个括号序列匹配的最小代价。定义 f l , r , x , y f_{l,r,x,y} 表示第一棵树的括号序列的区间[l,r]和第二颗树的区间[x,y]匹配的最小代价。一对括号没有被删除当且仅当左右括号都没有被删除。所以转移时我们可以从左到右考虑每个左括号,跳过右括号(右括号所在括号对已被删除)。转移时考虑三种情况:1.删除第一棵树的左括号。2.删除第二颗树的左括号。3.通过转化使两个左括号匹配。时间复杂度 O ( n 1 2 n 2 2 ) O(n_1^2n_2^2) 。考虑优化,对于一段正在考虑的区间,括号序列形如:
. . . . . . . . . . . . . . . (...)(...)...(...)(...)
为了尽量避开第二颗树的重儿子,我们可以比较最左端括号和最右端括号的大小,只考虑删除小一点的那个括号的左(右)括号。转移时跳过另一半已被删除的左右括号即可。可以证明,第二颗树的转移时间复杂度为 O ( s i z e ) = O ( n 2 l o g n 2 ) O(\sum size_{轻儿子})=O(n_2logn_2) ,第一棵树转移时间复杂度 O ( n 1 2 ) O(n_1^2) 。总时间复杂度为 O ( n 1 2 n 2 l o g n 2 ) O(n_1^2n_2logn_2)
代码:(递归写法,常数巨大)
由于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";
}
发布了106 篇原创文章 · 获赞 22 · 访问量 5478

猜你喜欢

转载自blog.csdn.net/weixin_44111457/article/details/102773598
今日推荐