[航海协会]万灵药

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[航海协会]万灵药相关的知识,希望对你有一定的参考价值。

万灵药


题解

首先容易发现,他题目中说的某两个前缀的最长公共后缀不就是我们正串 S A M SAM SAM上的节点吗?
但我们怎么求它们的最长公共前缀之和的答案呢?
有一种非常蠢的做法是,把我们反串的 S A M SAM SAM建出来,然后把正串上的所有结点在反串上建出它的虚树,显然,两个正串 S A M SAM SAM上的节点映射到反串 S A M SAM SAM节点的 l c a lca lca的深度就是它们的最长公共前缀的长度。

考虑一下上面的这个东西如何在加入删除的过程中维护答案。
显然,两个点割贡献可以看成这两个点到根的路径上重复的部分,那我们可以每加入一个点就把这个点到根路径上的每个点都加上 1 1 1,而一个点与别的点的贡献和就是它到根的这条路径上所有点的贡献之和。
删掉点的时候也只需要撤销掉它的贡献。
上面这东西显然是可以通过树链剖分来维护的。
这样是 O ( Q log ⁡ 2 n ) O\\left(Q\\log^2 n\\right) O(Qlog2n)的。
好像过不了 n ⩽ 5 × 1 0 5 n\\leqslant 5\\times 10^5 n5×105,没事,可以发现我们的所有操作都是在树链上进行的,我们可以用全局平衡二叉树来维护,就成功达到 O ( n log ⁡ n ) O\\left(n\\log n\\right) O(nlogn)了。注意一下,全局平衡二叉树事实上是平衡树的形式,而不是线段树,虽然两者复杂度上是没有区别的。

但笔者这样打了只会被疯狂卡常了。建两棵SAM还用倍增插点,不卡你卡谁
于是,笔者就去问了问神 O U Y E \\rm O\\colorredUYE OUYE,原来最开始完全没必要像我们刚刚说的那样建正反两棵 S A M SAM SAM,然后再去插虚树。
可以发现,你建出来的虚树任意一条边的长度都不会超过 1 1 1
映射到原树上,如果点对 ( x , y ) (x,y) (x,y) l c a lca lca深度为 d ( d > 0 ) d(d>0) d(d>0),那么点对 ( x − 1 , y − 1 ) (x-1,y-1) (x1,y1) l c a lca lca深度肯定是 d − 1 d-1 d1,它们都会被加入到虚树里面去,也就是说,我们虚树里面的深度肯定是连续的,而且根本不会新建任何虚点,正串 S A M SAM SAM中的所有点就可以构成虚树。
待って,我们上面不是虚树里的边不都是相当于在原串的后面多了一个字符吗?就是我们上面说的 ( x − 1 , y − 1 ) → ( x , y ) (x-1,y-1)\\rightarrow (x,y) (x1,y1)(x,y),这不就是正串 S A M SAM SAM里边的含义吗?
也就是说,正串 S A M SAM SAM D A G DAG DAG上刚好让长度加 1 1 1的边必然会出现在虚树里面,我们就可以直接通过正串 S A M SAM SAM构建出这棵树了。
再像上面说的那样建全局平衡二叉树即可。

时间复杂度 O ( n log ⁡ n ) O\\left(n\\log n\\right) O(nlogn)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned int uint;
#define MAXN 500005
#define MAXM 1000005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
char gc()static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
#define getchar gc
char obuf[1<<22],*opt=obuf+(1<<22);
void pc(const int&ch)*--opt=ch;
#define putchar pc
template<typename _T>
void read(_T &x)
   _T f=1;x=0;char s=getchar();
   while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
   while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
   x*=f;

template<typename _T>
void print(_T x)putchar('\\n');while(x>9)putchar((x%10)|'0');x/=10;putchar(x|'0');
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
LL gcd(LL a,LL b)return !b?a:gcd(b,a%b);
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,Q,dfn[MAXM],sz[MAXM],ltp[MAXM],idx,ip[MAXN],root[MAXM];
int father[MAXM],pre[MAXM],val[MAXM],wson[MAXM],dn[MAXM];LL ans,answer[MAXN];
char str[MAXN];bool vis[MAXM];
struct mingint lson,rson,len,mid,lzy,dif;LL sm,sum;;
struct nodeint ch[4],len,fa,ed;;
class SegmentTree
    private:
        ming tr[MAXM];int tot;
        void work(int rt,int aw)
            tr[rt].sum+=1ll*aw*tr[rt].dif;
            tr[rt].sm+=aw;tr[rt].lzy+=aw;
        
        void pushdown(int rt)
            if(tr[rt].lzy)
                if(tr[rt].lson)work(tr[rt].lson,tr[rt].lzy);
                if(tr[rt].rson)work(tr[rt].rson,tr[rt].lzy);
                tr[rt].lzy=0;
            
        
        void pushup(int rt)tr[rt].sum=tr[tr[rt].lson].sum+tr[tr[rt].rson].sum+tr[rt].sm;
    public:
        void build(int &rt,int l,int r)
            rt=++tot;int mid,all=(sz[pre[l]]-sz[wson[pre[r]]]+1)/2;
            for(mid=l+1;mid<=r;mid++)if(sz[pre[l]]-sz[pre[mid]]>all)break;
            mid--;tr[rt].mid=mid;tr[rt].dif=r-l+1;
            if(l<mid)build(tr[rt].lson,l,mid-1);
            if(r>mid)build(tr[rt].rson,mid+1,r);
        
        void insert(int rt,int l,int r,int al,int ar)
            if(al>r||ar<l||al>ar)return ;int mid=tr[rt].mid;
            if(al<=l&&r<=ar)ans+=tr[rt].sum;work(rt,1);return ;
            if(al<=mid&&mid<=ar)ans+=tr[rt].sm,tr[rt].sm++;
            pushdown(rt);
            if(al<mid)insert(tr[rt].lson,l,mid-1,al,ar);
            if(ar>mid)insert(tr[rt].rson,mid+1,r,al,ar);
            pushup(rt);
        
        void remove(int rt,int l,int r,int al,int ar)
            if(al>r||ar<l||al>ar)return ;int mid=tr[rt].mid;
            if(al<=l&&r<=ar)work(rt,-1);ans-=tr[rt].sum;return ;
            if(al<=mid&&mid<=ar)tr[rt].sm--,ans-=tr[rt].sm;
            pushdown(rt);
            if(al<mid)remove(tr[rt].lson,l,mid-1,al,ar);
            if(ar>mid)remove(tr[rt].rson,mid+1,r,al,ar);
            pushup(rt);
        
T;
vector<int>P[MAXM],G[MAXM];
class SuffixAutomaton
    public:
        node tr[MAXM];int tot,las,dep[MAXM],f[MAXM][20];
    public:
        void init()las=++tot;
        int extend(int x,int y)
            int p=las,np=las=++tot;tr[np].len=tr[p].len+1;tr[np].ed=y;
            for(;p&&!tr[p].ch[x];p=tr[p].fa)tr[p].ch[x]=np;
            if(!p)tr[np].以上是关于[航海协会]万灵药的主要内容,如果未能解决你的问题,请参考以下文章

[航海协会]青蛙题

[航海协会]基因切割

[航海协会]SSSP

[航海协会]稀疏阶乘问题

深度学习不是万灵药!神经网络3D建模其实只是图像识别?

[航海协会]身体