P7206 [COCI2019-2020#3] Lampice 题解
显然可以对答案奇偶分别二分,判断用点分治。考虑对每个点记录到当前分治中心的路径正着和倒着的 hash 值,那么两个点之间的路径是回文路径可以用一个简单的式子表示,移项一下把跟一个点有关的值放到一边,用 unordered_map 记录/查询即可,需要卡常,时间复杂度 \(\mathcal O(n\log^2n)\)。
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define mxn 50003
#define md 1000000007
#define pb push_back
#define mkp make_pair
#define ld long double
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int n,n1,mn,rt,ans,now,d[mxn],sz[mxn],son[mxn];
char s[mxn];
vector<int>g[mxn];
bool fl,v[mxn];
unsigned int h1[mxn],h2[mxn],c[mxn];
struct hash_pair{
template <class T1, class T2>
size_t operator()(const pair<T1, T2>& p) const{
auto hash1 = hash<T1>{}(p.first);
auto hash2 = hash<T2>{}(p.second);
return hash1 ^ hash2;
}
};
unordered_map<pair<unsigned int,int>,bool,hash_pair>mp;
void getrt(int x,int fa){
sz[x]=1;
int mx=0;
for(int i:g[x])if(i!=fa&&!v[i]){
getrt(i,x);
sz[x]+=sz[i];
mx=max(mx,sz[i]);
}
mx=max(mx,n1-sz[x]);
if(mx<mn)mn=mx,rt=x;
}
void dfs1(int x,int fa){
d[x]=d[fa]+1;
if(d[x]>now)return;
h1[x]=h1[fa]+(s[x]-'a'+1)*c[d[x]];
h2[x]=h2[fa]*27+s[x]-'a'+1;
if(mp[{h1[x]*c[now-d[x]-1]-h2[x],now-d[x]-1}]){
fl=1;
return;
}
for(int i:g[x])if(i!=fa&&!v[i]){
dfs1(i,x);
if(fl)return;
}
}
void dfs2(int x,int fa){
if(d[x]>now)return;
mp[{h1[x]*c[now-d[x]-1]-h2[x],d[x]}]=1;
for(int i:g[x])if(i!=fa&&!v[i]){
dfs2(i,x);
}
}
void dfs(int x,int n){
if(n<now)return;
n1=n,mn=1e9;getrt(x,0);x=rt;
v[x]=1,h2[x]=d[x]=0,h1[x]=s[x]-'a'+1;
mp.clear(),mp[{h1[x]*c[now-1],0}]=1;
for(int i:g[x])if(!v[i]){
dfs1(i,x);
if(fl)return;
dfs2(i,x);
}
for(int i:g[x])if(!v[i]){
dfs(i,sz[i]<sz[x]?sz[i]:n-sz[x]);
if(fl)return;
}
}
bool check(){
rep(i,1,n)v[i]=0;
fl=0;
dfs(1,n);
return fl;
}
signed main(){
scanf("%d%s",&n,s+1);
c[0]=1;
rep(i,1,n)c[i]=c[i-1]*27;
for(int i=1,x,y;i<n;++i){
scanf("%d%d",&x,&y);
g[x].pb(y),g[y].pb(x);
}
int l=0,r=(n-1)>>1;
while(l<r){
int mid=(l+r+1)>>1;now=(mid<<1)|1;
if(check())l=mid;
else r=mid-1;
}
ans=(l<<1)|1;
l=ans>>1,r=n>>1;
while(l<r){
int mid=(l+r+1)>>1;now=mid<<1;
if(check())l=mid;
else r=mid-1;
}
ans=max(ans,l<<1);
cout<<ans;
return 0;
}