BZOJ3103,BZOJ3350(Manacher,弦图染色)

传送门

题意:
给出一个由小写字母组成的字符串 S ,问有多少由小写字母
构成的字条串S,满足
1: |S|=|S|
2: S[L..R] 是回文串,当且仅当 S[L..R] 是回文串

题解:
http://foreseeable97.logdown.com/posts/194507-herbicidalontak2010palindromic-equivalence

首先manacher合并同类串很简单,关键是染色,如果是一般的无向图,很显然是一个NP完全问题,证明这张图是一张弦图:

i<j<k
然后我们已经知道了 ai!=ak
那么我们要证明如果有 ai!=aj ,那么 aj!=ak
首先根据连边的策略来看……
ai!=ak ,说明 [i+1,k1] 是回文串
所以 [kj+i+1,k1] [i+1,j1] 恰好成镜像关系
所以 [kj+i+1,k1] 也是回文串
那么根据回文关系…… a[kj+i]=a[j]
那么 a[kj+i] a[k] 的关系只有两种可能
要么 a[kj+i]!=a[k]
那么我们就得到 a[k]!=a[j] ,得证命题
否则 a[kj+i]=a[k]
那么 a[j]=a[k]
j k是在同一个联通块内
那么每一个联通块都是一个完全图!也就是一个弦图!
弦图的染色方案数是可以使用完美消除序列在 O(n) 的时间复杂度内解决的
有因为这题是完全图,所以任意一个 1n 的排列都是完美消除序列
于是直接从 1n 进行计算即可

#include<bits/stdc++.h>
using namespace std;
const int Maxn=1e6+50,mod=1e9+7;
typedef pair<int,int> pii;

int tot,n,m,r[Maxn*2],fa[Maxn],ans=1;
vector <pii> E;
char ch[Maxn],s[Maxn*2];
inline int getf(int x){return (fa[x]==x)?x:(fa[x]=getf(fa[x]));}
struct Edge{
    Edge* nxt;
    int to;
    Edge():nxt(NULL){}
}edge[Maxn*4];
int last[Maxn];
inline void merge(int i,int j)
{
    if(i==j||i&1||j&1||!i||j>2*n)return;
    fa[getf(i>>1)]=getf(j>>1);
}
inline void manacher()
{
    static int mx,p;
    mx=1,p=0;
    for(int i=1;i<=tot;i++)
    {
        r[i]=(mx>i)?(min(mx-i,r[p*2-i])):(1);
        while(s[i-r[i]]==s[i+r[i]])
        {
            merge(i-r[i],i+r[i]);
            r[i]++;
        }
        if(i-r[i]>0&&i+r[i]<=2*n&&!((i-r[i])&1))E.push_back(make_pair(i+r[i],i-r[i]));
        if(i+r[i]>mx)mx=i+r[i],p=i;
    }
}
inline void add(int x,int y)
{
    static int ecnt=0;
    edge[++ecnt].nxt=(&edge[last[x]]);
    edge[ecnt].to=y;
    last[x]=ecnt;
}
int main()
{   
    scanf("%s",ch+1);
    n=strlen(ch+1);
    for(int i=1;i<=n;i++)s[++tot]='#',s[++tot]=ch[i],fa[i]=i;
    s[0]='?';s[++tot]='#',s[++tot]='!';
    manacher();
    for(int e=E.size()-1;e>=0;e--)
    {
        int x=E[e].first>>1,y=E[e].second>>1;
        add(getf(x),(getf(y)));
        add(getf(y),(getf(x)));
    }
    for(int i=n;i>=1;i--)
    {
        if(getf(i)!=i)continue;
        static int cnt,vis[Maxn],vt=0;
        cnt=26;++vt;
        for(Edge *e=&edge[last[i]];e!=(&edge[0]);e=e->nxt)
        {
            int v=e->to;
            if(v<=i||vis[v]==vt)continue;
            vis[v]=vt;--cnt;
        }
        ans=1ll*ans*cnt%mod;
    }
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值