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}\).
看到异或,考虑拆位,然后点分治。

posted @ 2022-09-03 16:28  s1monG  阅读(31)  评论(0)    收藏  举报