【noi.ac #1997】A. 制胡竄

題目

Description
你有一顆nn個點的樹,樹的每一個節點上有一個小寫字母。你想知道,選擇樹上的一條簡單路徑(可以只包含一個點),使得經過的點上的字母構成的字符串是迴文串,這個迴文串的最大長度是多少?

Input format
第一行一個整數nn。

第二行一個長度爲nn的由小寫字母組成的字符串,其中第ii個字符表示ii號點上的字符。

接下來n−1n−1行每行兩個整數a,ba,b,代表樹上有一條邊(a,b)(a,b)。

Output format
一行一個整數,表示最長迴文串的長度。

Sample input 1
7
imanade
1 2
2 3
3 4
4 5
5 6
6 7
Sample output 1
3
Sample input 2
4
aabb
1 2
1 3
3 4
Sample output 2
2
Sample input 3
8
acdbabcd
1 6
6 7
6 3
3 4
4 5
5 2
8 5
Sample output 3
5
Constraints
保證1≤n≤500001≤n≤50000。保證SS只包含小寫字母。

本題採用子任務的方式評測。

子任務一(12pts12pts):n≤3000n≤3000。

子任務二(16pts16pts):保證ii號點和i+1i+1號點之間由一條邊(1≤i<n1≤i<n)。

子任務三(16pts16pts):保證樹上至多有100100個點的度爲11。

子任務四(56pts56pts):無額外限制。

思路

注意到如果存在長度爲nn的迴文串,則一定也存在長度爲n2n-2的迴文串。因此我們可以對串長分奇偶進行二分,然後檢查是否存在給定長度的迴文串。

首先考慮如何求經過給定點是否存在某個長度的迴文串。迴文串一定被這個點分成了兩半。我們枚舉長的這一半在哪個子樹裏,然後枚舉這個子樹中符合要求的點。這個點需要滿足兩個要求:

  1. 這個迴文串上面的一段是迴文串。
  2. 這個迴文串下面的一段在從根到某個其它子樹的路徑上出現了。

代碼

#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;

const int N=50077,base=131;
int n,root,st;
int mx[N],vis[N],siz[N],dep[N],totsize,p[N];
ll pw[N],h[N],rh[N];
char s[N];
vector<int> G[N],g[N];
int ans=1;
void get_rt(int u,int f){
	mx[u]=0,siz[u]=1;
	for(int v:G[u]) {
		if(vis[v]||v==f) continue;
		get_rt(v,u);
		siz[u]+=siz[v];
		mx[u]=max(mx[u],siz[v]);
	}
	mx[u]=max(mx[u],totsize-siz[u]);
	if(mx[root]>mx[u]) root=u;
} 
void recalc_siz(int u,int f){
	siz[u]=1;
	for(int v:G[u]) {
		if(vis[v]||v==f) continue;
		recalc_siz(v,u);
		siz[u]+=siz[v];
	}
}
void dfs_get(int u){
	recalc_siz(u,0);
	vis[u]=1;
	for(int v:G[u]){
		if(vis[v]) continue;
		totsize=siz[v],root=0;
		get_rt(v,0);
		g[u].push_back(root);
		dfs_get(root);
	}
}
gp_hash_table<ll,int> mp;
void get_hash(int u,int f){
	dep[u]=dep[f]+1;
	h[u]=h[f]*base+s[u]-'a'+1,rh[u]=(s[u]-'a'+1)*pw[dep[u]-1]+rh[f];
	mp[h[u]]++;
	for(int v:G[u]) {
		if(vis[v]||v==f) continue;
		get_hash(v,u);
	}
}
void clear(int u,int f,int val){
	mp[h[u]]+=val;
	for(int v:G[u]){
		if(vis[v]||v==f) continue;
		clear(v,u,val);
	}
}
bool get_ans(int u,int f,int l){
	p[dep[u]]=u;
	if(dep[u]>l) return 0;
	if(dep[u]>=l/2+1){
		int t=l-dep[u]+1;
		if(mp[h[u]-h[p[dep[u]-t]]*pw[t]]>0) {
			if(h[p[dep[u]-t+1]]==rh[p[dep[u]-t+1]]) {
				return 1;
			}
		}
	}
	for(int v:G[u]){
		if(v==f||vis[v]) continue;
		if(get_ans(v,u,l)) return 1;
	}
	return 0;
}
bool dfs(int u,int l){
	vis[u]=1;h[u]=dep[u]=0;mp.clear();
	get_hash(u,0);p[1]=u;
	for(int v:G[u]){
		if(vis[v]) continue;
		clear(v,u,-1);
		if(get_ans(v,u,l)) return 1;
		clear(v,u,1);
	}
	for(int v:g[u]) if(dfs(v,l)) return 1;
	return 0;
}
inline bool chk(int x){
	memset(vis,0,sizeof(vis));
	return dfs(st,x);
}
int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	pw[0]=1;
	for(int i=1;i<=n;i++) pw[i]=pw[i-1]*base;
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	mx[0]=n;
	root=0,totsize=n;get_rt(1,0);
	st=root;
	dfs_get(root);
	int l=1,r=n/2;
	while(l<=r){
		int mid=(l+r)>>1;
		if(chk(2*mid)) l=mid+1,ans=max(ans,2*mid);
		else r=mid-1;
	}
	l=1,r=(n-1)/2;
	while(l<=r){
		int mid=(l+r)>>1;
		if(chk(2*mid+1)) l=mid+1,ans=max(ans,2*mid+1);
		else r=mid-1;
	}
	printf("%d",ans);
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章