zoukankan      html  css  js  c++  java
  • CF741D Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths

    题目传送门

    分析:
    一个路径的字符串能够重排序构成回文串当且仅当其中最多有一种字符数量为奇数
    22种字符,按该种字符个数是否为奇数构成长度为22的二进制数
    其中就只有23种情况符合要求
    两条路径合并只需要按位异或了

    对于每个点(x),建立一个以从它开始向下延伸的路径的值作为下标的桶,
    一个一个儿子地做,每次加入一个儿子的子树时,先遍历这个子树的点(u),在前面儿子的子树构成的桶里面查询合法的下标中的值
    不难发现桶中代表节点(v)(u)(LCA)就是(x)
    这个桶存的值是目前最深的路径,当(u,x)确定时,我们当然是要贪心地选最深的点
    儿子的信息要往上合并

    其实可以考虑线段树合并,但是空间复杂度(O(nlogn))难以接受
    于是使用优雅的暴力:Dsu On Tree

    当我们需要对于每一个子树都进行遍历,并且信息可以用桶等方式进行(O(1))修改和查询时
    所有点共用一个桶,每棵子树暴力遍历修改,暴力查询,暴力清零
    显然复杂度为(O(sum_{i=1}^{n}sz[i])),其中(sz)为子树大小,这是(O(n^2))级别的,会超时
    儿子遍历完,开始遍历父亲时,我们可以选择最后一个儿子不清空,直接归并到父亲,父亲在遍历时就不遍历这个儿子的子树
    贪心地想,肯定是选重儿子不清空而归并
    这下子复杂度变成(O(sum_{i=1}^{n}sz[i]-sz[son[i]])),其中(son)表示重儿子
    这个复杂度不太好算,我们换个角度考虑
    对于每个点,如果暴力做上面的过程,那么他会被访问(dep)次,总复杂度为(O(n^2))
    如果使用dsu on tree,每个点到根的路径上,只有重链底端的点会访问它,也就是轻边的父节点
    又发现路径上的轻边是(O(logn))级别的,最终复杂度就变成了(O(nlogn))

    最终,dsu on tree的时间复杂度为(O(nlogn)),空间复杂度为(O(n))
    可以说是非常优雅的暴力

    本题查询复杂度为(O(23))总复杂度为(O(23nlogn))
    CF神仙机子肯定能跑(大嘘

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<vector>
    
    #define maxn 2000005
    #define MOD 1000000007
    #define INF 0x3f3f3f3f
    
    using namespace std;
    
    inline long long getint()
    {
        long long num=0,flag=1;char c;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
        while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
        return num*flag;
    }
    
    int n,q;
    int fir[maxn],nxt[maxn],to[maxn],len[maxn],cnt;
    int sz[maxn],dpt[maxn],son[maxn],pos[maxn],id[maxn],dis[maxn],cur;
    int b[1<<23],ans[maxn];
    
    inline void newnode(int u,int v,int w)
    {to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt,len[cnt]=w;}
    inline void dfs1(int u)
    {
    	sz[u]=1,pos[u]=++cur,id[cur]=u;
    	for(int i=fir[u];i;i=nxt[i])
    	{
    		dpt[to[i]]=dpt[u]+1,dis[to[i]]=dis[u]^len[i];
    		dfs1(to[i]),sz[u]+=sz[to[i]];
    		if(sz[son[u]]<sz[to[i]])son[u]=to[i];
    	}
    }
    
    inline void check(int u,int lca)
    {
    	if(b[dis[u]])ans[lca]=max(ans[lca],b[dis[u]]+dpt[u]-2*dpt[lca]);
    	for(int i=0;i<22;i++)if(b[dis[u]^(1<<i)])ans[lca]=max(ans[lca],b[dis[u]^(1<<i)]+dpt[u]-2*dpt[lca]);
    }
    
    inline void dfs2(int u,int kp)
    {
    	for(int i=fir[u];i;i=nxt[i])if(to[i]!=son[u])dfs2(to[i],0),ans[u]=max(ans[u],ans[to[i]]);
    	if(son[u])dfs2(son[u],1),ans[u]=max(ans[u],ans[son[u]]);
    	check(u,u);b[dis[u]]=max(b[dis[u]],dpt[u]);
    	for(int i=fir[u];i;i=nxt[i])if(to[i]!=son[u])
    	{
    		for(int j=pos[to[i]];j<pos[to[i]]+sz[to[i]];j++)check(id[j],u);
    		for(int j=pos[to[i]];j<pos[to[i]]+sz[to[i]];j++)b[dis[id[j]]]=max(b[dis[id[j]]],dpt[id[j]]);
    	}
    	if(!kp)for(int i=pos[u];i<pos[u]+sz[u];i++)b[dis[id[i]]]=0;
    }
    
    int main()
    {
    	n=getint();
    	for(int i=2;i<=n;i++)
    	{
    		int u=getint();
    		newnode(u,i,1<<(getchar()-97));
    	}
    	dfs1(1),dfs2(1,1);
    	for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'
    ':' ');
    }
    

  • 相关阅读:
    Html-浅谈如何正确给table加边框
    如何在移动设备上调试html5开发的网页
    swiper嵌套小demo(移动端触摸滑动插件)
    移动端如何用swiper实现导航栏效果
    background-color:transparent
    点击按钮 发送短信验证码后60秒倒计时
    placeholder的样式设置
    linux 定时任务crontab
    laravel学习一
    centos 7安装jdk
  • 原文地址:https://www.cnblogs.com/Darknesses/p/13054872.html
Copyright © 2011-2022 走看看