LOJ6066-哈希,dfs序(括号序列)

本文深入解析了LOJ6066题目的解题思路,采用树哈希和dfs序结合二分查找的算法策略,通过具体的代码示例详细阐述了解决方案。关键步骤包括构建树的括号序列、计算哈希值以及使用二分法确定最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LOJ6066

题目描述

题目描述

题解

首先想到的是树哈希,但其实不然,因为本题说儿子的顺序也要一致,因此我们考虑dfs序(即树的括号序列),我们将整棵树的 d f s dfs dfs序转换为一个序列,对其进行哈希,然后注意到 k k k值是具有二分单调性的(证明很显然),于是我们对 k k k值进行二分,然后 c h e c k check check
至于 c h e c k check check的方法就是:假设当前二分值为mid,那么我们将所有点挂在它的 m i d + 1 mid+1 mid+1级祖先上(开栈),这样每次计算每个节点的 u − m i d u-mid umid子树时可以方便加入合法的一段序列

代码

#include<bits/stdc++.h>
#include <tr1/unordered_map>
#define M 200009//要开双倍,因为dfs序 
#define LL unsigned long long
using namespace std;
using namespace tr1;
int read(){
	int f=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f; 
}
const LL h=37;
LL nxt[M],first[M],to[M],tot,cnt,n,num[M],len[M],ed[M],sta[M],top;
LL val[M],mi[M],has[M];
vector<LL>son[M];
unordered_map<LL,LL>mp;
void add(int x,int y){
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
void dfs1(int x){//求括号序列 
	num[x]=++cnt,val[cnt]=133;
	for(int i=first[x];i;i=nxt[i]){
		int v=to[i];
		dfs1(v);
		len[x]=max(len[x],len[v]);
	}ed[x]=++cnt,len[x]++,val[cnt]=233; 
}
void dfs2(int u,int k){//将点挂在k+1级祖先上 
	sta[++top]=u;
	if(top-1>k) son[sta[top-k-1]].push_back(u);
	for(int i=first[u];i;i=nxt[i]){
		int v=to[i];
		dfs2(v,k);
	}top--;
}
LL gethas(int l,int r){
	return has[r]-has[l-1]*mi[r-l+1];
}
bool check(int mid){
	mp.clear();
	for(int i=1;i<=n;i++) son[i].clear();
	dfs2(1,mid);
	for(int i=1;i<=n;i++)
		if(len[i]>mid){
			int l=num[i],r;LL ans=0;
			for(int j=0;j<son[i].size();j++){
				r=num[son[i][j]]-1;
				ans=ans*mi[r-l+1]+gethas(l,r);
				l=ed[son[i][j]]+1;
			}r=ed[i];
			ans=ans*mi[r-l+1]+gethas(l,r);
			if(mp.count(ans)) return true;
			mp[ans]=1;
		}return false;
	
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		int x=read(),y;
		for(int j=1;j<=x;j++) y=read(),add(i,y);
	}dfs1(1),mi[0]=1;
	for(int i=1;i<=cnt;i++) mi[i]=mi[i-1]*h;
	for(int i=1;i<=cnt;i++) has[i]=has[i-1]*h+val[i];
	int l=2,r=n;
	while(l<r){//满足二分单调性 
		int mid=(l+r+1)>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}printf("%d\n",l);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值