GDOI2016回文树

嗯,今年GD出题人和SC出题人都偶然的出了同一个算法的题啊。

精简的题目描述

有一棵树,每个点有一个小写字母,树上有些路径是回文串,求这样的树有多少棵。

将【SCOI2016萌萌哒】的模型放到树上。
给ST表每个块两个标号,分别表示正和反。
处理一个回文串的状态,首先将一条路径的两端先处理。
然后处理剩下的一条只有祖先的路,一开始我是用直观的方法,log^2合并的,后来发现很慢,就改成先将该路径分成log个块,存下每个块的开头和2的幂数,然后从尾开始合并。
然后,就跟【SCOI2016萌萌哒】差不多了。
由于我傻逼地不知道c++里log()很慢,导致超时了n次……

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)

using namespace std;

const int N = 100010;
const int mo = 1e+9+7;

typedef long long LL;

int h[N],n,tot,m;
struct edge{
    int x,next;
}e[N];
int k;
int fa[N*40];
int dep[N],u,f[N][20];
int q[N*40],p[N*40];
bool pd[N*40];
int que[N];
int v[20],op,ss[20];
int rank[N*40];
double lim;
int to[N];

void inse(int x,int y){
    e[++tot].x=y;
    e[tot].next=h[x];
    h[x]=tot;
}

void bfs(){
    int head=0,tail=1;
    que[1]=dep[1]=1;
    while(head<tail){
        int x=que[++head];
        fo(i,1,lim)f[x][i]=f[f[x][i-1]][i-1];
        for(int p=h[x];p;p=e[p].next)
            if (!dep[e[p].x]){
                dep[e[p].x]=dep[x]+1;
                f[e[p].x][0]=x;
                que[++tail]=e[p].x;
            }
    }
}

void prepare(){
    fo(i,0,(lim+1)*n*2+1){
        fa[i]=i;
        rank[i]=1;
    }
}

int lca(int x,int y){
    if (dep[x]<dep[y])swap(x,y);
    fd(i,lim,0)
    if (dep[f[x][i]]>=dep[y])x=f[x][i];
    if (x==y)return x;
    fd(i,lim,0)
    if (f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}

int getfather(int x){
    if (fa[x]==x)return x;
    return fa[x]=getfather(fa[x]);
}

int pt(int x,int y,int z){
    return (y*n+x)*2+z;
}

void tog(int x,int y){
    x=getfather(x),y=getfather(y);
    if (x==y)return;
    if (rank[x]>rank[y])fa[y]=x;
    else{
        fa[x]=y;
        if (rank[x]==rank[y])rank[y]++;
    }
}

int main(){
    freopen("paltree.in","r",stdin);
    freopen("paltree.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1){
        int x,y;
        scanf("%d%d",&x,&y);
        inse(x,y);
        inse(y,x);
    }
    lim=log(n)/log(2);
    bfs();
    prepare();
    scanf("%d",&m);
    fo(u,1,m){
        int x,y;
        scanf("%d%d",&x,&y);
        int tf=lca(x,y);
        fd(i,lim,0)
        if(dep[f[x][i]]>=dep[tf]&&dep[f[y][i]]>=dep[tf]){
            tog(pt(x,i,0),pt(y,i,0));
            x=f[x][i];
            y=f[y][i];
        }
        if (dep[x]<dep[y])swap(x,y);
        int len=dep[x]-dep[tf]+1;
        op=0;
        y=x;
        fd(i,lim,0)
        if (len>=(1<<i)){
            v[++op]=i;
            ss[op]=x;
            len-=(1<<i);
            x=f[x][i];
        }
        x=y;
        fd(i,op,1){
            tog(pt(x,v[i],0),pt(ss[i],v[i],1));
            x=f[x][v[i]];
        }       
    }
    fd(u,lim,1){
        fo(i,1,n){
            fo(j,0,1){
                int x=pt(i,u,j);
                int v=getfather(x);
                if (!pd[v]){
                    pd[v]=1;
                    q[v]=(x/2-1)%n+1;
                    p[v]=x%2;
                }
                else{
                    int a[2];
                    a[0]=pt(i,u-1,j);
                    a[1]=pt(f[i][u-1],u-1,j);
                    if (p[v]==j){
                        tog(pt(q[v],u-1,j),a[0]);
                        tog(pt(f[q[v]][u-1],u-1,j),a[1]);
                    }
                    else{
                        tog(pt(q[v],u-1,j^1),a[1]);
                        tog(pt(f[q[v]][u-1],u-1,j^1),a[0]);
                    }
                }
            }
        }
    }
    fo(i,1,n)tog(pt(i,0,0),pt(i,0,1));
    int tt=0;
    fo(i,1,n)
    if (getfather(i*2)/2==i)tt++;
    LL ans=1;
    fo(i,1,tt)(ans*=26)%=mo;
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

附状态,自己找亮点:
这里写图片描述
我不会提醒你:今天是5.20

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值