P4770 [NOI2018]你的名字

news/2024/10/22 18:31:59/

蒟蒻表示不会sam凉凉了,所以只能提高SA技巧?

题意:有一个串\(A\),每次选择一个\(A\)的子串\(A'\),以及串\(B\),问\(B\)的所有本质不同子串中不在\(A'\)中的串的数量。

(定义\(A_i\)表示以字符\(A_i\)开头的后缀,\(B_i\)同理)

\(B\)的本质不同字串显然是\(|B|*(|B|+1)/2\)了,然后要减去本质不同的在\(A'\)中的串

首先把所有串拼一起,把SA建出来,辣么就可以在SA中找到\(A'\)所对应的所有后缀,对于\(B\)对应的每个后缀\(B_i\),计算\(\max_{j=l}^r LCP(A_j,B_i)\),就是在\(A'\)中的前缀数量,加起来就是这个东西了,由于还要判重,所以计入的其实是\(\max(0,\max_{j=l}^r LCP(A_j,B_i)-LCP(B_i,next(B_{i})))\),其中\(next(B_i)\)意思是在\(B\)串的SA上\(B_i\)的后继

\(LCP(B_i,next(B_{i}))\)随便算是吧,现在要算的是\(\max_{j=l}^rLCP(A_j,B_i)\)

要算LCP的max值,显然只要在SA上求出前驱和后继计算就行了,那么要算区间前驱后继,就是二逼平衡树了

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
#define Log(x) (31-__builtin_clz(x))
il ll gi(){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;
}
char S[1600037];
int t[100010],lent[100010];
int n,N;
int ql[100010],qr[100010];
namespace SA{int x[1600037],y[1600037],_[1600037],SA[1600037],rk[1600037],ht[1600037],t[1600037];int st[21][1600037];il int LCP(int x,int y){if(!x||!y)return 0;if(x==y)return 1e9;int l=Log(y-x);return std::min(st[l][x],st[l][y-(1<<l)]);}il int gety(int x){return x<=N?y[x]:-1;}il vd getSA(){int set=128;for(int i=1;i<=N;++i)++t[x[i]=S[i]];for(int i=1;i<=set;++i)t[i]+=t[i-1];for(int i=N;i;--i)SA[t[x[i]]--]=i;for(int k=1;k<=N;k<<=1){int p=0;for(int i=N-k+1;i<=N;++i)y[++p]=i;for(int i=1;i<=N;++i)if(SA[i]>k)y[++p]=SA[i]-k;for(int i=0;i<=set;++i)t[i]=0;for(int i=1;i<=N;++i)++t[x[y[i]]];for(int i=1;i<=set;++i)t[i]+=t[i-1];for(int i=N;i;--i)SA[t[x[y[i]]]--]=y[i];memcpy(_,x,sizeof _);memcpy(x,y,sizeof _);memcpy(y,_,sizeof _);x[SA[1]]=p=1;for(int i=2;i<=N;++i){if(gety(SA[i])!=gety(SA[i-1])||gety(SA[i]+k)!=gety(SA[i-1]+k))++p;x[SA[i]]=p;}if(p>=N)break;set=p;}for(int i=1;i<=N;++i)rk[SA[i]]=i;for(int i=1,j,k=0;i<=N;++i){if(rk[i]==N)continue;if(k)--k;j=SA[rk[i]+1];while(S[i+k]==S[j+k])++k;ht[rk[i]]=k;}for(int i=1;i<=N;++i)st[0][i]=ht[i];for(int i=1;i<=Log(N);++i)for(int j=1;j+(1<<i)-1<=N;++j)st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<i-1)]);//for(int i=1;i<=N;++i)printf("%d %s\n",ht[i],S+SA[i]);}
}
#define mid ((l+r)>>1)
int rt[500010],ls[20000010],rs[20000010],sum[20000010],cnt;
il vd build(int&x,int l,int r){x=++cnt;if(l==r)return;build(ls[x],l,mid),build(rs[x],mid+1,r);
}
il vd update(int&x,int l,int r,const int&p){++cnt;ls[cnt]=ls[x],rs[cnt]=rs[x],sum[cnt]=sum[x];x=cnt;++sum[x];if(l==r)return;if(p<=mid)update(ls[x],l,mid,p);else update(rs[x],mid+1,r,p);
}
int pp;
il int query_nxt(int x,int y,int l,int r){if(!(sum[y]-sum[x]))return 0;if(l==r)return l;if(mid<pp)return query_nxt(rs[x],rs[y],mid+1,r);if(pp<l){if(sum[ls[y]]-sum[ls[x]])return query_nxt(ls[x],ls[y],l,mid);else return query_nxt(rs[x],rs[y],mid+1,r);}else{int t=query_nxt(ls[x],ls[y],l,mid);return t?t:query_nxt(rs[x],rs[y],mid+1,r);}
}
il int query_pre(int x,int y,int l,int r){if(!(sum[y]-sum[x]))return 0;if(l==r)return l;if(pp<=mid)return query_pre(ls[x],ls[y],l,mid);if(r<pp){if(sum[rs[y]]-sum[rs[x]])return query_pre(rs[x],rs[y],mid+1,r);else return query_pre(ls[x],ls[y],l,mid);}else{int t=query_pre(rs[x],rs[y],mid+1,r);return t?t:query_pre(ls[x],ls[y],l,mid);}
}
#undef mid
il bool check(int l,int r,int p,int k){if(l>r)return 0;pp=p;if(SA::LCP(query_pre(rt[l-1],rt[r],1,N),p)>=k)return 1;if(SA::LCP(p,query_nxt(rt[l-1],rt[r],1,N))>=k)return 1;return 0;
}
int lcp[1600037];
int main(){
#ifdef XZZSBfreopen("in.in","r",stdin);freopen("out.out","w",stdout);
#endifscanf("%s",S+1);n=strlen(S+1);N=n+1;int Q=gi();t[1]=n+1;S[n+1]='~';int sumt=0;for(int i=1;i<=Q;++i){scanf("%s",S+t[i]+1);ql[i]=gi(),qr[i]=gi();lent[i]=strlen(S+t[i]+1);t[i+1]=t[i]+lent[i]+1;N+=lent[i]+1;sumt+=lent[i];S[t[i]+lent[i]+1]='|';}SA::getSA();build(rt[0],1,N);for(int i=1;i<=n;++i)rt[i]=rt[i-1],update(rt[i],1,N,SA::rk[i]);int l,r;for(int o=1;o<=Q;++o){l=ql[o],r=qr[o];std::vector<int>sufs;for(int i=t[o]+1;i<=t[o]+lent[o];++i)sufs.push_back(SA::rk[i]);std::sort(sufs.begin(),sufs.end());ll res=1ll*lent[o]*(lent[o]+1)/2;for(int i=1;i<sufs.size();++i)res-=(lcp[SA::SA[sufs[i-1]]]=SA::LCP(sufs[i-1],sufs[i]));for(int i=t[o]+1,j=0;i<=t[o]+lent[o];++i){if(j)--j;while(check(l,r-j,SA::rk[i],j+1))++j;res-=std::max(0,j-lcp[i]);}printf("%lld\n",res);}return 0;
}

转载于:https://www.cnblogs.com/xzz_233/p/10651945.html


http://www.ppmy.cn/news/147412.html

相关文章

hdu4770 状态压缩+枚举

http://acm.hdu.edu.cn/showproblem.php?pid4770 Problem Description Harry: "But Hagrid. How am I going to pay for all of this? I havent any money." Hagrid: "Well theres your money, Harry! Gringotts, the wizard bank! Aint no safer place. Not…

BZOJ4770: 图样 (随机点值求异或最小生成树边权和)

题目描述&#xff1a; n ≤ 50 , m ≤ 8 n\le50,m\le8 n≤50,m≤8 题目分析&#xff1a; 根据最小生成树有小到大加入边可以将点按照二进制最高位分组&#xff0c;统计所有情况的边权和。 Code&#xff1a; #include<bits/stdc.h> #define maxn 55 #define maxm 9 u…

【JZOJ4770】闭门造车

Description 自从htn体验了一把飙车的快感&#xff0c;他就下定决心要闭门造车&#xff01;但是他两手空空怎么造得出车来呢?无奈的他只好来到了汽车零部件商店。 一走进商店&#xff0c;玲琅满目的各式零件看得htn眼花缭乱。但是他很快便反应过来:我只要买一套好的零件就行…

chmod 4770的第一位解释

权限标志通过三个“位”来定义&#xff0c;分别是&#xff1a; setuid&#xff1a;设置使文件在执行阶段具有文件所有者的权限。比如/usr/bin/passwd&#xff0c;如果一般用户执行该文件&#xff0c;则在执行过程中&#xff0c;该文件可以获得root权限&#xff0c;从而可以更改…

Jzoj4770 闭门造车

自从htn体验了一把飙车的快感&#xff0c;他就下定决心要闭门造车&#xff01;但是他两手空空怎么造得出车来呢?无奈的他只好来到了汽车零部件商店。 一走进商店&#xff0c;玲琅满目的各式零件看得htn眼花缭乱。但是他很快便反应过来:我只要买一套好的零件就行。首先它们的性…

BZOJ 4770: 图样(恶心概率期望DP)

题目 考试时推到 p p p就嫌麻烦&#xff0c;不想推了。。。 讲真写好DP预处理调一年。 时间复杂度 O ( n 4 m 2 m ) O(n^4m2^m) O(n4m2m)的代码 T E ( b u t c o r r e t ) C o d e \mathrm {TE (but \ corret)\ Code} TE(but corret) Code //#pragma GCC optimize(3) #inc…

bzoj 4770 图样 - 概率与期望 - 动态规划

题目传送门 传送门I 传送门II 题目大意 有一个$n$个点的完全图&#xff0c;每个点的权值是$[0, 2^{m})$中的随机整数&#xff0c;两点间的边的权值是两点点权的异或和&#xff0c;问它的最小异或生成树的边权和的期望。 考虑求最大异或生成树的分治做法&#xff0c;每次按最高位…

BZOJ4770 图样(概率期望+动态规划)

考虑求出所有MST的权值和再除以方案数&#xff0c;方案数显然是2mn。 按位考虑&#xff0c;显然应该让MST里的边高位尽量为0。那么根据最高位是0还是1将点集划分成两部分&#xff0c;整张图的MST就是由两部分各自的MST之间连一条最小边得到的。两部分的MST权值和可以dp得到&…