BZOJ5084: hashit
(很神仙的题啊 爆栈了 手动扩栈才过
题解:我们考虑题目实质上形成一颗trie树 查询的是trie树上每个节点到根路径上形成的字符串的子串中不同子串的个数 我们考虑每个点的价值=父亲节点的价值+这个点后缀不同子串的个数 那么我们建广义的后缀自动机在trie树上转移 然后查询当前节点在parent树中到根链的并集即可 那么我们考虑用set来维护树链的并 然后统计答案即可
#pragma comment(linker, "/STACK:102400000,102400000") #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define link(x) for(edge *j=h[x];j;j=j->next) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=2e5+10; const double eps=1e-8; #define ll long long using namespace std; // int size = 256 << 20; // 256MB // char *pp = (char*)malloc(size) + size; // __asm__("movl %0, %%esp\n" :: "r"(pp)); struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN<<1],*o=e; void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;} ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } typedef struct node{ int f,a[26]; vector<int>vec; }node; node d[MAXN]; int rt,cnt;char str[MAXN]; void newnode(int &x,int pre){ x=++cnt;d[x].vec.clear();d[x].f=pre; for(int i=0;i<26;i++)d[x].a[i]=0; } int cur,rt1,dis[MAXN<<1],fa[MAXN<<1][21],cnt1,ch[MAXN<<1][26],dep[MAXN<<1]; int pos[MAXN]; ll dist[MAXN<<1],ans[MAXN]; int built(int x){ int last=cur;cur=++cnt1;dis[cur]=dis[last]+1;int pp=last; for(;pp&&!ch[pp][x];pp=fa[pp][0])ch[pp][x]=cur; if(!pp)fa[cur][0]=rt1; else{ int q=ch[pp][x]; if(dis[q]==dis[pp]+1)fa[cur][0]=q; else{ int nt=++cnt1;dis[nt]=dis[pp]+1; memcpy(ch[nt],ch[q],sizeof(ch[q])); fa[nt][0]=fa[q][0];fa[q][0]=fa[cur][0]=nt; for(;ch[pp][x]==q;pp=fa[pp][0])ch[pp][x]=nt; } } return cur; } void dfs(int x){ for(int i=0;i<26;i++){ if(!d[x].a[i])continue; cur=pos[x];pos[d[x].a[i]]=built(i); dfs(d[x].a[i]); } } int p[MAXN<<1],fp[MAXN<<1],cnt2; set<int>s; set<int>::iterator ite,ip; void dfs1(int x,int deep){ dep[x]=deep+1;p[x]=++cnt2;fp[p[x]]=x; for(int i=1;i<=20;i++)fa[x][i]=fa[fa[x][i-1]][i-1]; link(x){ dist[j->t]=dist[x]+dis[j->t]-dis[x]; dfs1(j->t,deep+1); } } int Lca(int u,int v){ if(dep[u]<dep[v])swap(u,v); int tmp=dep[u]-dep[v]; for(int i=0;i<=20;i++)if(tmp&(1<<i))u=fa[u][i]; if(u==v) return u; for(int i=20;i>=0;i--){ if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i]; } return fa[u][0]; } void solve(int x,ll ans1){ for(int i=0;i<26;i++){ if(!d[x].a[i])continue; int xx=0,yy=0;ll t=ans1; ite=s.lower_bound(p[pos[d[x].a[i]]]); //printf("%d=====\n",s.size()); t+=dist[pos[d[x].a[i]]]; if(ite!=s.begin())ip=ite,ip--,xx=fp[(*ip)],t-=dist[Lca(xx,pos[d[x].a[i]])]; if(ite!=s.end())ip=ite,yy=fp[(*ip)],t-=dist[Lca(yy,pos[d[x].a[i]])]; if(xx&&yy)t+=dist[Lca(xx,yy)]; for(int j=0;j<d[d[x].a[i]].vec.size();j++)ans[d[d[x].a[i]].vec[j]]=t; s.insert(p[pos[d[x].a[i]]]); solve(d[x].a[i],t); } s.erase(p[pos[x]]); } int main(){ rt=cnt=0;cnt1=0;cnt2=0; scanf("%s",str+1);int len=strlen(str+1);newnode(rt,0); int temp=rt; for(int i=1;i<=len;i++){ if(str[i]=='-')temp=d[temp].f,d[temp].vec.pb(i); else{ int t=str[i]-'a'; if(!d[temp].a[t]) newnode(d[temp].a[t],temp); temp=d[temp].a[t]; d[temp].vec.pb(i); } } //cout<<cnt<<endl; //cout<<"sb"<<endl; pos[rt]=1;cnt1=rt1=1;dfs(rt); //cout<<cnt1<<endl; //cout<<"sb"<<endl; for(int i=1;i<=cnt1;i++)add(fa[i][0],i); dfs1(rt1,0); //cout<<"sb"<<endl; solve(rt,0); //for(int i=1;i<=len;i++) for(int i=1;i<=len;i++)printf("%lld\n",ans[i]); }
5084: hashit
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 223 Solved: 94
[Submit][Status][Discuss]
Description
你有一个字符串S,一开始为空串,要求支持两种操作
在S后面加入字母C
删除S最后一个字母
问每次操作后S有多少个两两不同的连续子串
Input
一行一个字符串Q,表示对S的操作
如果第i个字母是小写字母c,表示第一种加字母c的操作
如果为-表示删除操作,保证所有删除操作前S都非空
|Q|<=10^5
Output
输出|Q|行,第i行表示i个操作之后S内有多少个不同子串
Sample Input
aba-caba
Sample Output
1
3
5
3
6
9
12
17
3
5
3
6
9
12
17