CF1168D Anagram Paths

一、题目

点此看题

二、解法

挺有意思的结论题,首先我们搞一些 ( t observations)

  • 每个叶子的深度必须相同。
  • 有解的必要条件:设 (len[u])(u) 到叶子的距离,(f[u][c])(u) 到叶子的所有路径上字符 (c) 的最大出现次数,如果 (sum f[u][c]leq len[u]) 就有解,因为每条路径都必须有这些字符,而长度不够肯定不合法。

下证观察二是有解的充要条件,考虑归纳法充分性,但是直接归纳这个结论貌似不行,我们把结论改成:对于 (u) 到叶子的每条路径,有 (sum f[u][c]) 个位置字符固定,有 (len[u]-sum f[u][c]) 个位置字符任取

对于叶子该结论显然成立,对于一个儿子的节点显然成立。

对于二叉树的节点 (u),考虑它的两个儿子和连向儿子的两条边,儿子适用上述结论:

  • 对于字符 (c) 如果 (f[ls][c]<f[rs][c]),那么使用左儿子的自由位置把 (c) 字符调成和右儿子数量一样,如果 (f[ls][c]>f[rs][c]) 那么对右儿子做类似的调整,操作后的自由位置数是 (len[ls]-summax(f[ls][c],f[rs][c]))
  • 考虑连儿子的两条边如果都是问号,把这两个问号配对当作一个自由位置。
  • 考虑连儿子的两条边左儿子是问号右儿子是字符 (c),如果 (f[ls][c]>f[rs][c]) 那么右子树有一个自由位置不用调整了,可以和这个问好配对得到一个新的自由位置;如果 (f[ls][c]leq f[rs][c]) 那么把这个问号设置成字符 (c),自由位置不变;另一种情况同理可得。
  • 考虑连两个儿子的边都是字符,如果导致 (sum f[u][c]) 增加 (2) 那么两边都会减少一个自由位置,对于 (u) 来说就减少了一个自由位置;如果 (sum f[u][c]) 增加 (1) 那么势必自由位置数不改变;如果 (sum f[u][c]) 不变那么两边都减少一个自由位置,那么 (u) 增加了一个自由位置。

所以对于 (u) 满足自由位置数是 (len[u]-sum f[u][c]),可以归纳到所有情况。

那么现在的问题是带修维护 (sum f[u][c]),考虑修改一个点需要更新它的所有父亲。

考虑如果一个点只有一个儿子那么我们把它和它的儿子缩成一个点,那么现在树的深度最大是多少呢?因为每个叶子深度一样,这样构造的树是最深的:首先拿一条链,然后链上的每个节点连出一个分支,连到最深的地方,所以缩点之后最深的树深度是 (O(sqrt n))

那么缩点之后暴力跳祖先更新即可,时间复杂度 (O(nsqrt n))

三、总结

找结论一定要找必要条件,然后如果问题的背景是树形结构可以使用归纳法。

时间复杂度和某个量有关,可以找这个量的性质,比如本题缩点之后树深度有很好的性质。

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <iostream>
using namespace std;
const int M = 150005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,fk,ban,cnt[M][26],f[M][26];
int sum[M],a[M],fa[M],len[M],id[M];
vector<int> g[M];char s[10];
void dfs(int u)
{
	for(auto v:g[u])
	{
		dfs(v);
		if(len[u] && len[u]!=len[v]+1)
		{
			while(m--) puts("Fou");
			exit(0);
		}
		len[u]=len[v]+1;
	}
	id[u]=g[u].size()==1?id[g[u][0]]:u;
}
void upd(int u,int c,int w)
{
	cnt[u][c]+=w;if(u==id[1]) fk+=w;
	for(int x=fa[u];x;x=fa[x])
	{
		ban-=sum[x]>len[x];
		sum[x]-=f[x][c],f[x][c]=0;
		for(auto y:g[x])
			f[x][c]=max(f[x][c],f[y][c]+cnt[y][c]);
		sum[x]+=f[x][c];
		ban+=sum[x]>len[x];
	}
}
signed main()
{
	n=read();m=read();
	for(int i=2;i<=n;i++)
	{
		int j=read();scanf("%s",s);
		a[i]=s[0]-'a';
		g[j].push_back(i);
	}
	dfs(1);
	for(int i=1;i<=n;i++) if(id[i]==i)
		for(int &x:g[i]) fa[x=id[x]]=i;
	for(int i=2;i<=n;i++)
		if(a[i]>=0) upd(id[i],a[i],1);
	for(int i=1;i<=m;i++)
	{
		int u=read();scanf("%s",s);
		if(a[u]>=0) upd(id[u],a[u],-1);
		a[u]=s[0]-'a';
		if(a[u]>=0) upd(id[u],a[u],1);
		if(ban)
		{
			puts("Fou");
			continue;
		}
		printf("Shi ");
		int ans=0,fre=len[1]-sum[id[1]]-fk;
		for(int j=0;j<26;j++)
			ans+=(j+1)*(fre+f[id[1]][j]+cnt[id[1]][j]);
		printf("%d
",ans);
	}
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15152605.html