zoukankan      html  css  js  c++  java
  • 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);
    	}
    }
    
  • 相关阅读:
    AngularJs的UI组件ui-Bootstrap分享(十三)——Progressbar
    AngularJs的UI组件ui-Bootstrap分享(十二)——Rating
    AngularJs的UI组件ui-Bootstrap分享(十一)——Typeahead
    AngularJs的UI组件ui-Bootstrap分享(十)——Model
    AngularJs的UI组件ui-Bootstrap分享(九)——Alert
    AngularJs的UI组件ui-Bootstrap分享(八)——Tooltip和Popover
    AngularJs的UI组件ui-Bootstrap分享(七)——Buttons和Dropdown
    AngularJs的UI组件ui-Bootstrap分享(六)——Tabs
    Xamarin Error:Could not find android.jar for API Level 23.
    nodejs文件压缩-使用gulp命令(安装过程)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15152605.html
Copyright © 2011-2022 走看看