【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):无额外限制。
思路
注意到如果存在长度为nnn的回文串,则一定也存在长度为n−2n-2n−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;
}
【noi.ac #1997】A. 制胡窜相关推荐
- 【LOJ】#2479. 「九省联考 2018」制胡窜
题解 老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很 现在后缀树上线段树合并差点把我写死 主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了 但就是不想写 但是由于我过于老 ...
- NOI.AC NOIP模拟赛 第六场 游记
NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...
- NOI.AC#2007-light【根号分治】
正题 题目链接:http://noi.ac/problem/2007 题目大意 nnn个格子排成一排,每个格子有一个0/10/10/1和一个颜色.开始每个格子都是000,qqq次操作取反一个颜色的所有 ...
- NOI.AC#2139-选择【斜率优化dp,树状数组】
正题 题目链接:http://noi.ac/problem/2139 题目大意 给出nnn个数字的序列aia_iai.然后选出一个不降子序列最大化子序列的aia_iai和减去没有任何一个数被选中的 ...
- NOI.AC#2144-子串【SAM,倍增】
正题 题目链接:http://noi.ac/problem/2144 题目大意 给出一个字符串sss和一个序列aaa.将字符串sss的所有本质不同子串降序排序后,求有多少个区间[l,r][l,r][l ...
- NOI.AC#2266-Bacteria【根号分治,倍增】
正题 题目链接:http://noi.ac/problem/2266 题目大意 给出nnn个点的一棵树,有一些边上有中转站(边长度为222,中间有一个中转站),否则就是边长为111. mmm次询问一个 ...
- 野鸡NOI.AC模拟赛【2019.10.26】
前言 截止至2019.10.2614:222019.10.26\ \ \ \ 14:222019.10.26 14:22 成绩 正题 T1:NOI.AC−T1:NOI.AC-T1:NOI.AC− ...
- noi.ac 405 bzoj 4403 序列统计 题解
博客观赏效果更佳 题意简述 noi.ac再次蒯题,实锤了- 请你求长度在 [ 1 , n ] [1,n] [1,n] 范围内,值域在 [ l , r ] [l,r] [l,r] 范围内的序列中,不下降 ...
- noi.ac #543 商店
我们考虑可并堆维护,从深到浅贪心选取. 用priority_queue启发式合并的话,是60pts: #include<iostream> #include<cstdio> # ...
- noi.ac 邀请赛1 By cellur925
A. array 考场:上来就想暴力,首先第一个子任务肯定没问题,怎么搞都行.然后第二个子任务用个数组记下新修的值就行了.第三个子任务用一下等差数列求和公式帮助求解,每次都重新算(因为每次改变全部元素 ...
最新文章
- ecs安装tomcat和mysql_centos(Linux)系统阿里云ECS搭建 jdk,tomcat和MySQL环境,并部署web程序...
- JAVA中使用FTPClient实现文件上传下载
- 联盟和部落大战一触即发,你有票了吗?
- 第二十六讲:基础一开放封闭原则
- LiveVideoStackCon2019北京参会手册
- LeetCode OJ 147. Insertion Sort List
- 抗击肺炎,中国互联网公司在行动
- 3星难度-算式填符号
- 冯诺依曼结构和哈佛结构01
- 【译】JavaScript面试问题:事件委托和this
- text展示html,textview完美展示html格式代码
- win7与internet时间同步出错_【时间同步出错】win7系统同步internet时间总是提示同步时出错的解决方法...
- 数据敏捷,HTAP数据库既决效率又决生死
- Java剑开天门(二)
- java poi 读取word_Java poi读取word文档(本篇只能读取text内容)
- 精准DNA甲基化/羟甲基化测序(oxBS-seq)|易基因技术推介
- Docker 从入门到实践系列一 - 什么是Docker
- 做Web自动化前,你必会的几个技能
- 用java做小学数学系统_基于jsp的小学数学试卷生成-JavaEE实现小学数学试卷生成 - java项目源码...
- 卡尔曼滤波五个公式推导