标签:sign namespace code root 一段 最小 复杂度 struct 方法
题意:
胖头鱼是一条喜欢摸鱼的鱼。
他经常去河边摸鱼,每一天,他会选择一段连续的时间去摸鱼,河里有很多不同种类鱼,每一个时间点会有恰好一条鱼出现,而胖头鱼一定能摸到这条鱼(如果他在摸鱼的话)。
摸完鱼后,他会把摸到的鱼按摸到的时间顺序排成一排,统计今天摸鱼的收益,由于他是个鱼盲,他只能分辨出哪些鱼是同一个种类的,而不会知道一条鱼到底是什么种类的,所以他会把鱼按照种类用正整数标号,同一种鱼用同一个标号,不同的鱼用不同的标号,聪明的胖头鱼会选择字典序最小的标号方法。
胖头鱼发现在不同的时间摸鱼也可能有相同的收益,两次收益不同当且仅当摸到的鱼的数量不同或者存在某个i使得摸到的第i条鱼的标号不同。
胖头鱼可以在任意时间开始摸鱼,任意时间结束摸鱼,但是会至少摸一条鱼。他想让你统计他一共有多少种可能的不同的收益。(他一定只会选择一段连续的时间摸鱼)。
题解:
本题其实就是求另类的本质不同的子串个数。
考虑如果是一般的本质不同怎么做呢,弄一个后缀数组,然后height之和就是重复的个数。
这题其实也差不多,只不过排序的cmp有点复杂。
如果我们要比较两个后缀,那么我们先分别暴力将它搞成字典序最小的标号方法,然后直接比较,接下来操作就和上面说的一样。只不过这个做法肯定是会超时的。我们要解决的问题就是如何快速比较后缀,如果我们将hash表示成$\sum (next_i-i) \times p^i$,我们发现,hash值相同的就是同构的。
那么如何维护这个hash值,使用可持久化线段树维护,这样我们就得到了一个快速判断是否同构的方法。
接下来我们在排序的时候进行二分hash比较大小,即可在$O(nlog^3n)$的时间复杂度内解决问题。
#include<cstdio> #include<vector> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; const int INF=131333131; int n,a[50002],head[50002],las[50002],nex[50002],rt[50002],cnt,t[50002]; vector<int>g[50002]; long long ans; unsigned int jc[50002],sum; typedef struct{ unsigned int sum; int ls,rs; }P; P p[30000002]; void build(int root,int begin,int end){ if (begin==end) { p[root].sum=0; return; } int mid=(begin+end)/2; p[root].ls=++cnt;p[root].rs=++cnt; build(p[root].ls,begin,mid);build(p[root].rs,mid+1,end); p[root].sum=p[p[root].ls].sum*jc[end-mid]+p[p[root].rs].sum; } void gengxin(int root,int las,int begin,int end,int wz,int z){ if (begin==end) { p[root].sum=z-begin; return; } int mid=(begin+end)/2; if (wz<=mid) { p[root].rs=p[las].rs;p[root].ls=++cnt; gengxin(p[root].ls,p[las].ls,begin,mid,wz,z); } else { p[root].ls=p[las].ls;p[root].rs=++cnt; gengxin(p[root].rs,p[las].rs,mid+1,end,wz,z); } p[root].sum=p[p[root].ls].sum*jc[end-mid]+p[p[root].rs].sum; } void chaxun(int root,int begin,int end,int begin2,int end2){ if (!root) { sum*=jc[min(end,end2)-max(begin,begin2)+1]; return; } if (begin>=begin2 && end<=end2) { sum=sum*jc[end-begin+1]+p[root].sum; return; } int mid=(begin+end)/2; if (!(mid<begin2 || begin>end2))chaxun(p[root].ls,begin,mid,begin2,end2); if (!(end<begin2 || mid+1>end2))chaxun(p[root].rs,mid+1,end,begin2,end2); } unsigned int cx(int x,int y){ sum=0; chaxun(rt[y],1,n,x,y); return sum; } int ef(int num,int x,int y,int z){ int mid; while(x<y) { mid=(x+y)/2; if (g[num][mid]<z)x=mid+1;else y=mid; } return g[num][x]; } bool cmp(const int& x,const int& y){ int lef=0,righ=min(n-x+1,n-y+1),mid; while(lef<righ) { mid=(lef+righ+1)/2; if (cx(x,x+mid-1)==cx(y,y+mid-1))lef=mid; else righ=mid-1; } if (lef==min(n-x+1,n-y+1))return (x>y); return (ef(a[x+lef],0,g[a[x+lef]].size()-1,x)-x+1<ef(a[y+lef],0,g[a[y+lef]].size()-1,y)-y+1); } int main() { scanf("%d",&n);jc[0]=1;ans=(long long)n*(n+1)/2; for (int i=1;i<=n;i++)jc[i]=jc[i-1]*INF; for (int i=1;i<=n;i++){scanf("%d",&a[i]);g[a[i]].push_back(i);} memset(head,-1,sizeof(head)); for (int i=1;i<=n;i++) { if (head[a[i]]!=-1) { las[i]=head[a[i]];nex[head[a[i]]]=i; } head[a[i]]=i; } for (int i=1;i<=n;i++) { rt[i]=++cnt; if (!las[i]) { p[rt[i]].ls=p[rt[i-1]].ls;p[rt[i]].rs=p[rt[i-1]].rs; } else gengxin(rt[i],rt[i-1],1,n,las[i],i); } for (int i=1;i<=n;i++)t[i]=i; stable_sort(t+1,t+n+1,cmp); for (int i=2;i<=n;i++) { int x=t[i-1],y=t[i]; int lef=0,righ=min(n-x+1,n-y+1),mid; while(lef<righ) { mid=(lef+righ+1)/2; if (cx(x,x+mid-1)==cx(y,y+mid-1))lef=mid; else righ=mid-1; } ans-=lef; } printf("%lld\n",ans); return 0; }
标签:sign namespace code root 一段 最小 复杂度 struct 方法
原文地址:https://www.cnblogs.com/1124828077ccj/p/12329413.html