【Codeforces 914E】 Palindromes in a Tree

题目描述:https://codeforces.ml/contest/914/problem/E

题目大意:

给出一棵树,每个点有一个字母权值[a,z],定义回文路径:这条路径上的权值,经过重排后,可以得到一个回文。

输出每个点,在多少条回文路径上

Note:一个点也算一条,所以每个点答案至少为1

题目思路

用到了刚学的算法:树上点分治。

考虑一条链在什么情况下满足条件:最多只有一个字母出现奇数次。

所以我们维护当前这个条件:用二进制状压每一个字母,如果当前状态为2的幂或者为0即合法

对于一个根节点来说,可以用合并的思想算一下,经过当前根节点的与不经过当前根节点的合法路径

因为需要还原路径。

所以处理出,子树路径的异或值。

然后每次处理当前子树时,就把当前子树的异或值全部减掉,如果存在一个答案,那么说明这个节点有一个答案。

由于合并的链最少也是以根为端点,所以路径上的点有递归的效应:3->4 如果4存在一个答案,那么3也要加4的答案。

所以说,对于每一颗子树而言,都用回溯算一下。

最后不要忘记根节点的贡献:经过根节点的条数/2+根节点为端点的条数

PS:会爆ll

Code:

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=1e17;
const int maxn=1e6+6;
const int mod=998244353;
const double eps=1e-3;
inline bool read(ll &num)
{char in;bool IsN=false;
    in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
int sz[maxn],mx[maxn],rt=0,sum;
int vis[maxn],dfn=0;
ll t[maxn*5];
vector<int>v[maxn];
ll res[maxn];
char str[maxn];
int c[maxn];
bool f[maxn*5];
void getrt(int u,int fa){
    mx[u]=0;sz[u]=1;
    for(int e:v[u]){
        if(e==fa||vis[e]) continue;
        getrt(e,u);
        sz[u]+=sz[e];
        mx[u]=max(mx[u],sz[e]);
    }
    mx[u]=max(mx[u],sum-sz[u]);
    if(mx[rt]>mx[u]) rt = u;
}
void getdis(int u,int fa,int s,int val){
    if(u!=fa) t[s]+=val;
    for(int e:v[u]){
        if(e==fa||vis[e]) continue;
        getdis(e,u,s^c[e],val);
    }
}
ll tempy = 0,tempx = 0;
ll go(int u,int fa,int s,int val){
    ll ans = t[s^val];
   // printf("%d %d\n",u,s);
    for(int i=0;i<20;i++){
        int x = 1<<i;
        ans+=t[s^x^val];
    }
    tempy+=ans;

    int judgex = s^val;
    //debug(judgex);
    if(f[judgex]){
        tempx++;
        ans++;
    }
    for(int e:v[u]){
        if(e==fa||vis[e]) continue;
        ans+=go(e,u,s^c[e],val);
    }
    res[u]+=ans;
    return ans;
}
void calc(int u){
   // printf("--------------------%d\n",u);
    getdis(u,u,0,1);
  //  for(int i=0;i<=4;i++)
  //      printf("%d : %d\n",i,t[i]);
    tempy = tempx = 0;
    for(int e:v[u]){
        if(vis[e]) continue;
        getdis(e,u,c[e],-1);
   //     printf("%d:\n",e);
   //     for(int i=0;i<=4;i++)
  //          printf("%d : %d\n",i,t[i]);
        go(e,u,c[e],c[u]);
        getdis(e,u,c[e],1);
    }
 //   debug(tempx);
  //  debug(tempy);
    res[u]+=tempy/2+tempx;

    getdis(u,u,0,-1);

}
void Solve(int u){
   // debug(u);
    vis[u]=1;calc(u);
    for(int e:v[u]){
        if(vis[e]) continue;
        sum = sz[e];mx[rt = 0] = n;
        getrt(e,u);
        Solve(rt);
    }
}
int main(){
    read(n);
    for(int i=1;i<=n-1;i++){
        int x,y;scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    f[0]=true;
    for(int i=0;i<20;i++) f[1<<i]=true;
    scanf("%s",str+1);
    for(int i=1;i<=n;i++){
        int x = str[i]-'a';
        c[i] = 1<<x;
    }
    ///restart is OK
    sum = mx[rt=0]=n;
    getrt(1,0);
    getrt(rt,rt);
    Solve(rt);
    for(int i=1;i<=n;i++) printf("%lld ",res[i]+1);
    return 0;
}
/**

**/

最近应该会更新树上点分治的题目比较频繁.补一下这个知识!

顺带的还有dsu on a tree与树剖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章