【线段树】The Preliminary Contest for ICPC Asia Xuzhou 2019 Colorful String(回文树+线段树+状压/bitset)
下午比赛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\n",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\n",ans); return 0; }
( 之前用思修课学后缀数组,这回用毛概课学回文树
2019-09-10 20:37:12