22.9.3 总结
A
求字符串插入多少字符后可以变为回文串。
将字符串翻转后与原字符串求最长公共子串。
\(ans=\min(i+j-2*f_{i,j}).(i+j=n-(n\mod 2))\)
code
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=2010;
char s1[N],s2[N];
int f[N][N];
int n,res;
int main() {
scanf("%s",s1+1); n=strlen(s1+1);
res=n-1;
for(int i=n; i>=1; i--) s2[i]=s1[n-i+1];
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(s1[i]==s2[j]) f[i][j]=f[i-1][j-1]+1;
f[i][j]=max(f[i][j],max(f[i-1][j],f[i][j-1]));
if(i+j==n-n%2) res=min(res,i+j-2*f[i][j]);
}
}
printf("%d\n",res);
return 0;
}
B
C
在一颗树上,有 \(q\) 次询问,每次询问独立。
删除某条边 \(v,fa_v\),询问某个点 \(u\) 以下内容。
是否到达根节点;若否,是否到达任意特殊点并求出距离。
第一个询问,则是问 \(v\) 是否在 \(1->u\) 路径上,求 \(Lca\) 即可。
第二个询问,我们先钦定数组 \(f_u\) 代表 \(u\) 子树内最近特殊,\(dis_u\) 为 其到根节点距离。
这可以 \(O(n)\) 预处理。
这样就求出子树内特殊点的答案了。
对于子树外特殊点 \(w\), 我们怎么处理呢?
令 \(g\) 为 \(Lca_{u,w}\)
计算为 \(dis_u+dis_w-2*dis_g=dis_u-dis_g+f_g\).
就是求所有 \(g\) 中最大的 \(-dis_g+f_g\).
树上倍增即可。
code
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=1e5+10,logn=21,inf=2e9;
int tot,head[N],nxt[2*N],edge[2*N],ver[2*N];
int n,m,q,s[N],fa[N],dis[N],p[N],depth[N];
int f[N][logn],g[N][logn];
void addedge(int x,int y,int z) {
ver[++tot]=y;
edge[tot]=z;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int u,int father) {
if(s[u]) p[u]=0;
for(int i=head[u]; i; i=nxt[i]) {
int v=ver[i],z=edge[i];
if(v==father) continue;
dis[v]=dis[u]+z;
dfs1(v,u);
p[u]=min(p[u],p[v]+z);
}
}
void dfs2(int u,int father) {
f[u][0]=father;
depth[u]=depth[father]+1;
if(p[u]<inf) g[u][0]=-dis[u]+p[u];
else g[u][0]=p[u];
for(int i=1; i<logn; i++) {
f[u][i]=f[f[u][i-1]][i-1];
g[u][i]=min(g[u][i-1],g[f[u][i-1]][i-1]);
}
for(int i=head[u]; i; i=nxt[i]) {
int v=ver[i];
if(v==father) continue;
dfs2(v,u);
}
}
int solve(int u,int v) {
int res=inf,w=u;
if(depth[u]<depth[v]) return -1;
for(int i=logn-1; i>=0; i--) {
if(depth[f[u][i]]>=depth[v]) {
res=min(res,g[u][i]);
u=f[u][i];
}
}
res=min(res,g[u][0]);
if(u==v) return res+dis[w];
else return -1;
}
int main() {
scanf("%d%d%d",&n,&m,&q);
for(int i=1; i<=n; i++) p[i]=inf;
for(int i=1,z; i<=m; i++) {
scanf("%d",&z);
s[z]=1;
}
for(int i=2,z; i<=n; i++) {
scanf("%d%d",&fa[i],&z);
addedge(fa[i],i,z);
addedge(i,fa[i],z);
}
dfs1(1,1);
dfs2(1,1);
for(int i=1,u,v; i<=q; i++) {
scanf("%d%d",&v,&u);
int ans=solve(u,v);
if(ans==-1||v==1) puts("Yep");
else {
if(ans>=inf) puts("Nop");
else printf("%d\n",ans);
}
}
return 0;
}
D
在树上,求 \(\sum_{1\le i<j\le n} (a_i⊕a_j)\times dis_{i,j}\).
看到异或,考虑拆位,然后点分治。