码迷,mamicode.com
首页 > 其他好文 > 详细

dtoi4670 浑水摸鱼

时间:2020-02-19 00:55:07      阅读:57      评论:0      收藏:0      [点我收藏+]

标签: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;
}

dtoi4670 浑水摸鱼

标签:sign   namespace   code   root   一段   最小   复杂度   struct   方法   

原文地址:https://www.cnblogs.com/1124828077ccj/p/12329413.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!