zoukankan      html  css  js  c++  java
  • 【线段树】The Preliminary Contest for ICPC Asia Xuzhou 2019 Colorful String(回文树+线段树+状压/bitset)

    Colorful String

    下午比赛TLE,一直很纳闷为什么线段树+回文树会T,然后晚上发现我线段树写错一行。然后气哭QAQ。

    113m赛后过,不会T。

    下面代码用的是bitset,也可以直接状压,毕竟才26位。

    线段树是记录[l,r]区间的状态,最后返回状态再得到1的数量。

    回文树还是那个回文树。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=3e5+50;
    typedef long long ll;
    /*******线段树***********/ 
    struct P{
        int l,r;
        bitset<30>bit;
    }tree[maxn<<2];
    inline void build(int l,int r,int k)
    {
        tree[k].l=l;tree[k].r=r;   
        if(tree[k].l==tree[k].r)return; 
        int mid=(tree[k].l+tree[k].r)>>1;
        build(l,mid,k<<1);
        build(mid+1,r,k<<1|1);
    }
    inline void add(int x,int u,int k)
    {
        if(tree[k].l==tree[k].r)
        {
            tree[k].bit.set(u);return;
        }
        int mid=(tree[k].l+tree[k].r)>>1;
        if(x<=mid)add(x,u,k<<1);
        else add(x,u,k<<1|1);
        tree[k].bit=tree[k<<1].bit|tree[k<<1|1].bit;
    }
    inline bitset<30> find(int l,int r,int k)
    {
        if(tree[k].l==l&&tree[k].r==r)return tree[k].bit;
        if(tree[k].l==tree[k].r)
        {
            return tree[k].bit;
        }
        int mid=(tree[k].l+tree[k].r)>>1;
        if(r<=mid)return find(l,r,k<<1);
        if(l>mid)return find(l,r,k<<1|1);
        return find(l,mid,k<<1)|find(mid+1,r,k<<1|1);
    }
    /***************************************/
    char s[maxn];//存储主串 
    ll ans1[maxn],cnt[maxn],ans;
    int i,j,k,m,n,o,p,q,js,jl,tot,last;
    int len[maxn],fail[maxn],ch[maxn][26];
    inline int newnode(int x)
    {
        tot++;
        len[tot]=x;
        return(tot);
    }
    inline int getfail(int x,int n)
    {
        while(s[n-len[x]-1]!=s[n])x=fail[x];
        return(x);
    }
    int main()
    {
        int u;
        scanf("%s",s+1);
        s[0]=-1;fail[0]=1;last=0;
        len[0]=0;len[1]=-1;tot=1;
        int num=strlen(s+1);
        build(1,num,1);
        for(i=1;i<=num;i++)
        {
            u=s[i]-'a';
            add(i,u,1);
        }
        for(i=1;i<=num;i++)
        {
            s[i]-='a';
            p=getfail(last,i);
            if(ch[p][s[i]]==0)
            {
                q=newnode(len[p]+2);
                fail[q]=ch[getfail(fail[p],i)][s[i]];
                ch[p][s[i]]=q;
                int kkk=min(((2*i-len[q]+1)>>1)+1,i);
                ans1[ch[p][s[i]]]=find(i-len[q]+1,kkk,1).count();
            }
            last=ch[p][s[i]];
            cnt[ch[p][s[i]]]++;
        }
        ans=0;
        for(i=tot;i>1;i--)
        {
            cnt[fail[i]]+=cnt[i];
            ans+=cnt[i]*ans1[i];
        }
        printf("%lld
    ",ans);
        return 0;
    } 

    还是好气啊。怪我太着急,一T就慌了。得改。

    要好好和队友合作,不能急凸(`⌒´メ)凸

    2019-09-08  00:08:30


    然后去好好理解了回文树。

    直接回文树可以过这道题了。参考师兄的话:每加一个字符判断该字符在last节点是否出现过,可以不用线段树。

    搞不明白,为啥子题解还要dfs呢..... 

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=3e5+50;
    typedef long long ll;
    
    char s[maxn];//存储主串 
    ll ans1[maxn],cnt[maxn],ans;
    int i,j,k,m,n,o,p,q,js,jl,tot,last;
    //last节点是当前节点的上一个节点 
    int len[maxn],fail[maxn],ch[maxn][26];
    //len[i]:节点i的回文串长度
    //fail[i]:指向节点i的最长后缀回文串的节点 
    int bit[maxn]={0},tt;
    inline int newnode(int x)
    {
        tot++;
        len[tot]=x;
        return(tot);
    }
    inline int getfail(int x,int n)
    {
        while(s[n-len[x]-1]!=s[n])x=fail[x];
        //设s[i]为字母y 找yAy的A串所在节点 
        return(x);
    }
    inline int getsize(int x)
    {
        int ans=0;
        for(int i=0;i<=26&&(1<<i)<=x;i++)
            if(x&(1<<i))ans++;
        return ans;
    }
    int main()
    {
        int u;
        scanf("%s",s+1);
        s[0]=-1;fail[0]=1;last=0;
        len[0]=0;len[1]=-1;tot=1;
        int num=strlen(s+1);
        for(i=1;i<=num;i++)
        {
            s[i]-='a';
            p=getfail(last,i);
            if(ch[p][s[i]]==0)//p节点没有一条标有s[i]字符的边 
            {
                tt=bit[p];
                tt|=(1<<s[i]);
                q=newnode(len[p]+2);
                bit[q]=tt;
                //新建节点q 节点q长度为p节点长度+2:
                //因为 yAy;A是p节点所代表的回文串 
                fail[q]=ch[getfail(fail[p],i)][s[i]];
                //如果p节点的最长回文后缀有一条标有s[i]字符的边,
                //则这条边所连的节点所代表的的回文串就是q节点的最长回文后缀 
                ch[p][s[i]]=q;
                ans1[ch[p][s[i]]]=getsize(bit[q]);
            }
            last=ch[p][s[i]];
            cnt[ch[p][s[i]]]++;
        }
        ans=0;
        for(i=tot;i>1;i--)
        {
            cnt[fail[i]]+=cnt[i];
            ans+=cnt[i]*ans1[i];
        }
        printf("%lld
    ",ans);
        return 0;
    }

    ( 之前用思修课学后缀数组,这回用毛概课学回文树

    2019-09-10 20:37:12

  • 相关阅读:
    slqite3练习
    QStackedWidget 与 QStackedLayout 的用法区别
    pyqt5 菜单,工具栏,线程,matplotlib
    PyQt5 结合 matplotlib 时,如何显示其 NavigationToolbar
    tkinter事件高级用法实例
    tkinter菜单图标,工具栏
    tkinter界面卡死的解决办法
    8个经过证实的方法:提高机器学习模型的准确率
    结合Scikit-learn介绍几种常用的特征选择方法
    scikit-learn的主要模块和基本使用
  • 原文地址:https://www.cnblogs.com/kkkek/p/11484022.html
Copyright © 2011-2022 走看看