【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與樹剖

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