Codecraft-18 and Codeforces Round #458 E. Palindromes in a Tree(点分治)

题意:

     一棵树,每个点都有一个字母,如果一条路径的字符的某个排列是回文串,就称这个路径是回文的,问经过每个点的回文路径有多少条

思路:

     很明显的点分治。首先因为字母是a到t,所以可以状压一下,利用异或来改变路径的状态,那么任意一个路径都可以用一个值来表示,如果一个值的二进制中1的个数小于等于1,那么这个路径就是回文的(利用__builtin_popcount函数)。
    然后根据树分治的思想,我们选取了一个重心,我们先把以重心为根,所有子树的所有路径通过状压后的值用一个数组记录数量。我们这里要做的,就是统计通过某个点的回文路径有多少条。当我们要算某个子树的贡献时,我们就先删去这个子树的所有的路径的值的贡献,然后统计答案,统计完了再把这些值放回去。
     但是存在一个问题,就是当我们发现dfs到某个点的时候,路径构成的值可以构成一个回文路径时,需要把重心到这个点的路径上的所有点的答案都加上一个值。这里可以通过dfs的时候顺便记录子树的贡献,记录完后把值返还给父亲即可。
     剩下的就是一些小细节了(其实细节还蛮多的),写的时候注意一下即可

错误及反思:

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 200100;

struct EDGE{
    int to,next;
}e[N*2];
int first[N],n,tot,si[N],maxn[N],k;
long long ans[N];
bool did[N];
char s[N];
long long m[2000000];
vector<int> v;

void addedge(int x,int y){
    e[tot].to=y;
    e[tot].next=first[x];
    first[x]=tot++;
    e[tot].to=x;
    e[tot].next=first[y];
    first[y]=tot++;
}

void dfs_size(int now,int fa){
    si[now]=1;
    maxn[now]=0;
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to]){
            dfs_size(e[i].to,now);
            si[now]+=si[e[i].to];
            maxn[now]=max(maxn[now],si[e[i].to]);
        }
}

void dfs_root(int now,int fa,int& root,int& nu,int t){
    int MA=max(maxn[now],si[t]-si[now]);
    if(MA<nu){
        nu=MA;
        root=now;
    }
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to])
            dfs_root(e[i].to,now,root,nu,t);
}

void dfs2(int now,int fa,int tlen){
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to])
            dfs2(e[i].to,now,tlen^(1<<(s[e[i].to]-'a')));
    v.push_back(tlen);
}

long long dfs3(int now,int fa,int tlen,int root){
    long long num=0;

    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to]){
            num+=dfs3(e[i].to,now,tlen^(1<<(s[e[i].to]-'a')),root);
        }

    ans[now]+=num;
    for(int k=0;k<20;k++){
        num+=m[(1<<k)^tlen^(1<<(s[root]-'a'))];
        ans[now]+=m[(1<<k)^tlen^(1<<(s[root]-'a'))];
    }

    num+=m[tlen^(1<<(s[root]-'a'))];
    ans[now]+=m[tlen^(1<<(s[root]-'a'))];

    return num;
}

long long dfs4(int now,int fa,int tlen,int root){
    long long num=0;
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].to!=fa&&!did[e[i].to]){
            num+=dfs4(e[i].to,now,tlen^(1<<(s[e[i].to]-'a')),root);
        }
    if(__builtin_popcount(tlen^(1<<(s[root]-'a')))<=1) num++;
    ans[now]+=num;
    return num;
}


void solve(int now){
    int root,nu=1e9;
    dfs_size(now,-1);
    dfs_root(now,-1,root,nu,now);
    did[root]=true;
    for(int i=first[root];i!=-1;i=e[i].next){
        if(!did[e[i].to]){
            v.clear();
            dfs2(e[i].to,root,(1<<(s[e[i].to]-'a')));
            for(int j=0;j<v.size();j++) m[v[j]]++;
        }
    }
    long long td=0;
    for(int i=first[root];i!=-1;i=e[i].next){
        if(!did[e[i].to]){
            v.clear();
            dfs2(e[i].to,root,(1<<(s[e[i].to]-'a')));
            for(int j=0;j<v.size();j++) m[v[j]]--;

            td+=dfs3(e[i].to,root,(1<<(s[e[i].to]-'a')),root);

            ans[root]+=dfs4(e[i].to,root,(1<<(s[e[i].to]-'a')),root);

            for(int j=0;j<v.size();j++) m[v[j]]++;
        }
    }
    ans[root]+=td/2;

    for(int i=first[root];i!=-1;i=e[i].next){
        if(!did[e[i].to]){
            v.clear();
            dfs2(e[i].to,root,(1<<(s[e[i].to]-'a')));
            for(int j=0;j<v.size();j++) m[v[j]]--;
        }
    }

    for(int i=first[root];i!=-1;i=e[i].next)
        if(!did[e[i].to])
            solve(e[i].to);
}


void init(){
    memset(first,-1,sizeof(first));
    tot=0;
}

int main(){
    scanf("%d",&n);
    init();
    for(int i=0,u,v;i<n-1;i++){
        scanf("%d%d",&u,&v);
        addedge(u,v);
    }
    scanf("%s",s+1);
    solve(1);
    for(int i=1;i<=n;i++) printf("%lld ",ans[i]+1);
}

猜你喜欢

转载自blog.csdn.net/roll_keyboard/article/details/80691338