浑水摸鱼

Posted xjqxjq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浑水摸鱼相关的知识,希望对你有一定的参考价值。

题目描述

胖头鱼是一条喜欢摸鱼的鱼。

他经常去河边摸鱼,每一天,他会选择一段连续的时间去摸鱼,河里有很多不同种类鱼,每一个时间点会有恰好一条鱼出现,而胖头鱼一定能摸到这条鱼(如果他在摸鱼的话)。

摸完鱼后,他会把摸到的鱼按摸到的时间顺序排成一排,统计今天摸鱼的收益,由于他是个鱼盲,他只能分辨出哪些鱼是同一个种类的,而不会知道一条鱼到底是什么种类的,所以他会把鱼按照种类用正整数标号,同一种鱼用同一个标号,不同的鱼用不同的标号,聪明的胖头鱼会选择字典序最小的标号方法。

胖头鱼发现在不同的时间摸鱼也可能有相同的收益,两次收益不同当且仅当摸到的鱼的数量不同或者存在某个 $i$ 使得摸到的第 $i$ 条鱼的标号不同。

胖头鱼可以在任意时间开始摸鱼,任意时间结束摸鱼,但是会至少摸一条鱼。他想让你统计他一共有多少种可能的不同的收益。(他一定只会选择一段连续的时间摸鱼)。

数据范围

$1 le a_i le n le 5 imes 10^4$

题解

考虑如果我们可以把每个后缀用最小表示出来,然后进行排序,答案为 $frac{n imes (n+1)}{2}-sum lcp(str_i,str_{i+1})$ 。

考虑把 $hash$ 表示成 $sum (next_i-i) imes p^i$ ,其中 $next_i$ 为和 $i$ 相同的数的下一个位置,如果没有的话就设为 $i$ ,那两个最小表示的串相同的话 $hash$ 就可以体现其相同。

考虑用主席树维护子串的 $hash$ 值,然后排序时二分 $hash$ (二哈分析)即可。

效率: $O(nlog^3n)$

代码

#include <bits/stdc++.h>
#define LL long long
#define U unsigned long long
using namespace std;
const int N=5e4+5,M=2e6+5;
const U B=793999;
int n,a[N],ls[M],rs[M],T[N],t,p[N],nx[N];
LL ans; U s[M],b[N]; set<int>S[N];
#define mid ((l+r)>>1)
void upd(int &x,int y,int l,int r,int v,U w){
    x=++t;s[x]=s[y]+w;
    ls[x]=ls[y];rs[x]=rs[y];
    if (l==r) return;
    if (mid>=v) upd(ls[x],ls[y],l,mid,v,w);
    else upd(rs[x],rs[y],mid+1,r,v,w);
}
U qry(int x,int l,int r,int L,int R){
    if (L<=l && r<=R) return s[x];
    if (mid>=R) return (ls[x]?qry(ls[x],l,mid,L,R):0);
    if (mid<L) return (rs[x]?qry(rs[x],mid+1,r,L,R):0);
    return (ls[x]?qry(ls[x],l,mid,L,R):0)+(rs[x]?qry(rs[x],mid+1,r,L,R):0);
}
U hs(int l,int r){return (T[l]?qry(T[l],1,n,l,r):0);}
int lcp(int x,int y){
    int l=0,r=n-max(x,y)+1;
    while(l<r){
        int i=(l+r+1)>>1;
        if (hs(x,x+i-1)*b[x]==hs(y,y+i-1)*b[y]) l=i;
        else r=i-1;
    }
    return l;
}
int Pos(int x,int i){
    return (*S[a[i]].lower_bound(x))-x;
}
bool cmp(int x,int y){
    int l=lcp(x,y);
    if (x+l>n) return 1;if (y+l>n) return 0;
    return Pos(x,x+l)<Pos(y,y+l);
}
int main(){
    cin>>n;b[0]=1;
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]),b[i]=b[i-1]*B,
        p[i]=i,S[a[i]].insert(i);
    for (int i=n;i;i--){
        T[i]=T[i+1];
        if (nx[a[i]]) upd(T[i],T[i],1,n,
            nx[a[i]],b[n-i+1]*(nx[a[i]]-i));
        nx[a[i]]=i;
    }
    stable_sort(p+1,p+n+1,cmp);ans=1ll*n*(n+1)/2;
    for (int i=1;i<n;i++) ans-=lcp(p[i],p[i+1]);
    printf("%lld
",ans);return 0;
}

以上是关于浑水摸鱼的主要内容,如果未能解决你的问题,请参考以下文章

摸鱼篇 | 这款自动化工具带你高效做事,优雅摸鱼

这款黑科技,不会代码也能玩自动化,高效摸鱼

揭秘:一个月不摸鱼能写多少代码?

基于SSM源代码风格Java代码生成器 Maven版开箱即用 摸鱼神器

摸鱼神器!开发效率提升 1000% 的 Vue 低代码表单组件

摸鱼神器!开发效率提升 1000% 的 Vue 低代码表单组件