1 条题解
-
0
自动搬运
来自洛谷,原作者为

RainWetPeopleStart
IX搬运于
2025-08-24 23:06:09,当前版本为作者最后更新于2024-11-18 10:10:29,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
赛时 70pts 没调完,最后只写了 50pts。
下文中 表示空串(其中 是字符串,)。
这里稍微改一下前缀的定义(为方便叙述):
字符串 是字符串 的前缀当且仅当存在 且 为整数使得 。
15pts
首先,发现三元组的个数是 的,考虑暴力枚举三元组,但此时不好 chk。
那如何 chk 呢。考虑 DP,设 表示 是否合法,转移时枚举新加的串的位置即可,复杂度 。(不过这个东西貌似可以bitset优化,可能能过 30)
30pts
发现一个状态表示一个 bool 十分浪费,考虑优化。发现若 合法,则可以通过添加空串的方式使 合法,可以设 表示使 合法的最小 值,就有转移 (要求 是某个 的前缀)。
判断前缀可以使用哈希,复杂度 ,用 trie 则为 。(实际上加剪枝后在原题数据中可以拿 50pts)
50pts
考虑优化转移,上文中合法的 构成了一段区间,此时可以预处理出 表示最大的 使 是某个 的前缀(因为 是空串,故 一定存在)。
此时 的上界为 。由于对查 而言,左端点是递减的,可以使用单调栈上二分,复杂度 。
70pts
此时状态数会炸,考虑将状态与 挂钩。
发现对于 来说,使 合法的 构成一段区间,考虑交换状态和答案,记 表示使 合法的最大的 。
转移如下:
。
其中 。方便起见,可设 。
转移就相当于 RMQ,可以 ST 表维护。
如何求 呢,注意到 具有可二分性,使用字符串哈希 chk 即可。
答案为 $\sum\limits_{i=1}^{|T|}\sum\limits_{j=1}^Kf_{i,j}+1-i$。
下面考虑第二问,每一个 值对答案的贡献形如区间加等差数列,将其变为在二重差分数组上单点改,最后还原即可。
复杂度 。
100pts
发现 max 里面 那一维是 1,能不能依此优化?
可以的,记 表示区间 中使 取最大值的 。可以将转移过程大致抽象为:
1.对当前区间求 。
2.更新 为 并转移到 。
此时,我们发现, 的 值一定不会成为 max。
修改 定义,设 表示 中使 取最大值的 。
依此建 的边,发现建出来的是根节点为自环的外向“森林”,转移到 就相当于跳父亲(定义根节点父亲为自身)。
设当前在点 ,则所对 值为 。
我们求出 表示 。 表示有多少个 值跳到了 ,前者可以树上倍增,后者可以树上差分求,可以依据这两个东西来做第二问,依据 做第一问。
综上,本题在 的时间复杂度内得到解决。
代码
#include<bits/stdc++.h> #define ll long long #define pii pair<int,int> #define fi first #define se second #define mk make_pair using namespace std; const int N1=5050,N=5e5+10,SG=30; int n,K;int sub; int to[N]; int hs[12][N],ln[12],pb[N]; int base=131,mod=1011451423; int hsh[N]; int gh(int l,int r){ return (hsh[r]-1ll*pb[r-l+1]*hsh[l-1]%mod+mod)%mod; }ll g[N]; int tr[N]; struct segtree{ pii T[N<<2]; #define ls p*2 #define rs p*2+1 #define mid (l+r)/2 void pu(int p){ T[p]=max(T[ls],T[rs]); } void bd(int p,int l,int r){ if(l==r){ T[p]=mk(to[l],-l);return ; }bd(ls,l,mid);bd(rs,mid+1,r);pu(p); }pii qry(int p,int l,int r,int pl,int pr){ if(pl<=l&&r<=pr) return T[p]; pii res=mk(-1,0); if(pl<=mid) res=max(res,qry(ls,l,mid,pl,pr)); if(pr>mid) res=max(res,qry(rs,mid+1,r,pl,pr)); return res; } #undef ls #undef rs #undef mid }sgt; vector<int> E[N]; int dep[N]; int fa[N][22];ll sm[N][22]; bool vis[N]; ll val[N],sum[N],tg[N]; void dfs(int u,int pr){ vis[u]=1;dep[u]=dep[pr]+1; fa[u][0]=pr;sm[u][0]=to[u]; for(int i=1;i<=19;i++) fa[u][i]=fa[fa[u][i-1]][i-1],sm[u][i]=sm[u][i-1]+sm[fa[u][i-1]][i-1]; for(auto v:E[u]) if(!vis[v]) dfs(v,u); }void dfs2(int u){ vis[u]=1; for(auto v:E[u]){ if(!vis[v]) dfs2(v),val[u]+=val[v]; } } void slv1(){ cin>>n>>K; for(int i=1;i<=n;i++){ string s;cin>>s; ln[i]=s.length(); int hsh=0; for(int j=0;j<ln[i];j++){ hsh=1ll*hsh*base%mod; hsh=(hsh+s[j]-'a')%mod; hs[i][j+1]=hsh; } }string t;cin>>t; int len=t.length(); t=' '+t; for(int i=1;i<=len;i++){ hsh[i]=1ll*hsh[i-1]*base%mod+t[i]-'a'; hsh[i]%=mod; }pb[0]=1;for(int i=1;i<=len;i++) pb[i]=1ll*pb[i-1]*base%mod; for(int i=1;i<=len;i++){ int l=i-1,r=len; while(l<r){ int mid=(l+r+1)/2; int hsh1=gh(i,mid),lenn=mid-i+1,cnt=0; for(int j=1;j<=n;j++){ if(lenn>ln[j]) continue; if(hsh1==hs[j][lenn]) cnt++; } if(cnt>0) l=mid; else r=mid-1; }to[i]=l; }to[len+1]=len; sgt.bd(1,1,len+1); for(int i=1;i<=len;i++) tr[i]=-sgt.qry(1,1,len+1,i,to[i]+1).se; for(int i=1;i<=len;i++) E[tr[i]].push_back(i); for(int i=len;i>=1;i--) if(!vis[i]) dfs(i,i); ll ans=0; for(int i=1;i<=len;i++){ ll s=0;int now=i; for(int j=19;j>=0;j--){ if((K>>j)&1) s+=sm[now][j],now=fa[now][j]; }ans+=s;sum[i]=s; if(dep[i]>K){ val[i]++,val[now]--; }else{ val[i]++,val[now]--;tg[now]+=K-(dep[i]-1); }vis[i]=0;sum[i]+=K;sum[i]-=1ll*K*i; //cout<<i<<' '<<now<<endl; } ans+=1ll*len*K;ans-=1ll*K*(1ll*len*(len+1)/2); for(int i=len;i>=1;i--) if(!vis[i]) dfs2(i); for(int i=1;i<=len;i++){ val[i]+=tg[i];//cout<<i<<' '<<val[i]<<' '<<dep[i]<<endl; g[i+1]-=K;g[i]+=sum[i];g[i+1]-=sum[i];g[to[i]+2]+=val[i]; } for(int i=1;i<=len;i++) g[i]+=g[i-1]; for(int i=1;i<=len;i++) g[i]+=g[i-1]; cout<<ans<<endl;for(int i=1;i<=len;i++) cout<<g[i]<<' '; } int main(){ cin>>sub; slv1(); return 0; }
- 1
信息
- ID
- 10959
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者