当前位置: 首页 > news >正文

【[NOI2018]你的名字】

题目

可能是一个乱搞做法,同时也跪求有人能帮我分析一下复杂度

还是先来看比较简单的\(68pts\),也就是\(l=1,r=|S|\)的情况

我们可以直接把\(S\)串和所有的\(T\)串一起建一个广义\(SAM\),用一个\(vector\)维护每个\(T\)加入\(SAM\)时新产生的节点

我们只需要求出来这些新增节点没有在\(S\)串出现的本质不同的子串个数就好了

我们提前处理好每一个节点的\(endpos\),标记一下其是否在\(S\)中出现过

对于那些新出现在\(SAM\)上的节点\(x\)我们可以直接判断一下其是否在\(S\)中出现过,经典操作自然是\(ans+=len[x]-len[fa[x]]\)

但是考虑到\(x\)\(parent\)树上的祖先自然也在当前的\(T\)中出现过,于是我们还需要考虑这些节点的贡献

倍增?看起来好像非常可行,但是还有一个问题,就是判重

显然在处理一个\(T\)的时候\(parent\)树上的一个节点不能被计算两次,于是在\(parent\)树上倍增又不太可行了,因为不太方便我们打标记来判重

倍增不行我们就暴力啊,我们直接暴力访问\(x\)的祖先们,一旦有一个祖先在之前被访问过或者是在\(S\)中出现过,那么我们就不在往上跳了

至于复杂度我也不知道是什么,我甚至都觉得这个样子最坏会导致每次都把\(parent\)树遍历一遍,所以求有神仙能帮忙分析一下这个玄学的复杂度

\(68pts\)代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 3000005
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
char S[maxn],T[maxn];
int n,m,l,r,cnt=1,lst=1,num;
struct E{int v,nxt;}e[maxn];
std::vector<int> v[100005];
int fa[maxn],len[maxn],endpos[maxn],son[maxn][26],head[maxn],vis[maxn];
int top,st[maxn];
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
inline void ins(int c,int o)
{
    int p=++cnt,f=lst; lst=p;
    len[p]=len[f]+1,endpos[p]=o;
    if(o>1) v[o-1].push_back(p);
    while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    if(!f) {fa[p]=1;return;}
    int x=son[f][c];
    if(len[f]+1==len[x]) {fa[p]=x;return;}
    int y=++cnt;
    if(o>1) v[o-1].push_back(y);
    len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
    for(re int i=0;i<26;i++) son[y][i]=son[x][i];
    while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
void dfs(int x) {if(endpos[x]!=1) endpos[x]=0; for(re int i=head[x];i;i=e[i].nxt) dfs(e[i].v),endpos[x]|=endpos[e[i].v];}
int main()
{
    scanf("%s",S+1);n=strlen(S+1);
    for(re int i=1;i<=n;i++) ins(S[i]-'a',1);
    scanf("%d",&m);
    for(re int i=1;i<=m;i++)
    {
        scanf("%s",T+1),n=strlen(T+1);
        scanf("%d%d",&l,&r);
        lst=1;
        for(re int j=1;j<=n;j++) 
            ins(T[j]-'a',i+1);
    }
    for(re int i=2;i<=cnt;i++) add(fa[i],i); dfs(1);
    for(re int i=1;i<=m;i++)
    {
        LL ans=0;top=0;
        for(re int j=0;j<v[i].size();j++)
        {
            int t=v[i][j];
            if(endpos[t]||vis[t]) continue;
            ans+=len[t];st[++top]=t;vis[t]=1;
            while(!vis[fa[t]]&&fa[t]&&!endpos[fa[t]]) t=fa[t],vis[t]=1,st[++top]=t;
            ans-=len[fa[t]];
        }
        for(re int j=1;j<=top;j++) vis[st[j]]=0;
        printf("%lld\n",ans);
    }
    return 0;
}

再来看看剩下的非特殊情况

看到\(S\)串里的区间我们就知道我们不能只是粗略维护\(endpos\)集合了,我们有时候甚至得关心\(endpos\)里到底有哪些元素

于是我们得用一个可持久化数据结构来维护一下\(endpos\)集合

这里选择的是主席树,至于做法和上面差不多

我们还是对\(SAM\)上新增的节点以及其祖先算贡献,每次用主席树找出当前节点\(x\)\(endpos\)集合里小于等于\(r\)的最大值\(now\)

根据\(now\)\(l\)的关系进行讨论

  1. 如果\(now-len[fa[x]]>=l\),那么就说明当前这个节点表示的子串里已经有一些完全出现在了\([l,r]\)中,所以我们没有必要往上进行了,在这里计算贡献就好了,减掉那些完全出现在\([l,r]\)里的子串,也就是\(now-l+1\),但是可能这个节点根本产生不了这些子串,于是需要和\(len[x]\)取一个\(min\)

  2. 否则的话这个节点表示的子串里没有一个完全出现在\([l,r]\)中,所以还要继续算下去

但是这样每次都需要在主席树里二分,可以加一个小优化,一旦\(endpos\)集合的最大值小于\(l\),或者最小值大于\(r\),我们就不在主席树里二分了

这里的复杂度和上面相比多了一个\(log\),于是更加玄学了,并不保证代码能随时不T

交上去能获得取决于评测机稳定程度的分数的代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 3000005
#define M 10000005
#define re register
#define LL long long
inline int min(int a,int b) {return (a<b)?a:b;}
inline int max(int a,int b) {return (a>b)?a:b;}
char S[500005],T[500005];
int n,m,cnt=1,lst=1,num,U,__,tot;
struct E{int v,nxt;}e[maxn];
std::vector<int> a[100005];
int x[100005],y[100005];
int fa[maxn],len[maxn],endpos[maxn],son[maxn][26],head[maxn];
int rt[maxn],sz[maxn],to[maxn],_to[maxn],mx[maxn],tx[maxn];
unsigned short vis[maxn];
int top,st[500005];
int l[M],r[M],d[M];
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
inline void ins(int c,int pos,int o)
{
    int p=++cnt,f=lst; lst=p;
    len[p]=len[f]+1,endpos[p]=tx[p]=mx[p]=pos;
    if(o) a[o].push_back(p);
    while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    if(!f) {fa[p]=1;return;}
    int x=son[f][c];
    if(len[f]+1==len[x]) {fa[p]=x;return;}
    int y=++cnt;
    len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
    for(re int i=0;i<26;i++) son[y][i]=son[x][i];
    while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
void dfs(int x)
{
    to[x]=++__;_to[__]=x;sz[x]=1;
    if(!tx[x]) tx[x]=U+1;
    for(re int i=head[x];i;i=e[i].nxt) 
    {
        int v=e[i].v;
        dfs(v),sz[x]+=sz[v];
        mx[x]=max(mx[v],mx[x]);
        tx[x]=min(tx[x],tx[v]);
    }
}
int change(int pre,int x,int y,int pos)
{
    int root=++tot;
    d[root]=d[pre]+1;
    if(x==y) return root;
    l[root]=l[pre],r[root]=r[pre];
    int mid=x+y>>1;
    if(pos<=mid) l[root]=change(l[pre],x,mid,pos);
        else r[root]=change(r[pre],mid+1,y,pos);
    return root;
}
int query(int p1,int p2,int x,int y,int pos)
{
    if(x==y) return d[p2]-d[p1];
    int mid=x+y>>1;
    if(pos<=mid) return query(l[p1],l[p2],x,mid,pos);
    return d[l[p2]]-d[l[p1]]+query(r[p1],r[p2],mid+1,y,pos);
}
int ask(int p1,int p2,int x,int y,int k)
{
    if(x==y) return x;
    int now=d[l[p2]]-d[l[p1]];
    int mid=x+y>>1;
    if(k>now) return ask(r[p1],r[p2],mid+1,y,k-now);
    return ask(l[p1],l[p2],x,mid,k);
}
inline int find(int X,int o) 
{
    if(y[o]<tx[X]||x[o]>mx[X]) return -1;
    int Y=to[X]+sz[X]-1;
    X=to[X];
    int T=query(rt[X-1],rt[Y],1,U,y[o]);
    if(!T) return -1;return ask(rt[X-1],rt[Y],1,U,T);
}
int main()
{
    scanf("%s",S+1);n=strlen(S+1);U=n;
    for(re int i=1;i<=n;i++) ins(S[i]-'a',i,0);
    scanf("%d",&m);
    for(re int i=1;i<=m;i++)
    {
        scanf("%s",T+1),n=strlen(T+1);
        scanf("%d%d",&x[i],&y[i]);lst=1;
        for(re int j=1;j<=n;j++) ins(T[j]-'a',0,i);
    }
    for(re int i=2;i<=cnt;i++) add(fa[i],i); dfs(1);
    for(re int i=1;i<=cnt;i++)
        if(endpos[_to[i]]) rt[i]=change(rt[i-1],1,U,endpos[_to[i]]);else rt[i]=rt[i-1];
    for(re int i=1;i<=m;i++)
    {
        LL ans=0;top=0;int now=0;
        for(re int j=0;j<a[i].size();j++)
        {
            int t=a[i][j];
            if(vis[t]) continue;
            vis[t]=1,st[++top]=t;ans+=len[t];
            now=find(t,i);
            if(now!=-1&&now-len[fa[t]]>=x[i]) 
            {ans-=min(len[t],now-x[i]+1);continue;}
            while(1) 
            {
                if(vis[fa[t]]||!fa[t]) {ans-=len[fa[t]];break;}
                t=fa[t],vis[t]=1,st[++top]=t;
                now=find(t,i);
                if(now!=-1&&now-len[fa[t]]>=x[i]) {ans-=min(now-x[i]+1,len[t]);break;}
            }
        }
        for(re int j=1;j<=top;j++) vis[st[j]]=0;
        printf("%lld\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/asuldb/p/10284379.html

相关文章:

  • OmniPlan 3 Pro密钥
  • AI书单
  • web通用测试点总结
  • d3生成的树状图
  • Tushare模块
  • 详解Oracle partition分区表
  • centos配置NTP服务器
  • 跟我一起学机器学习:机器学习常用流程-1
  • [Leetcode] 寻找数组的中心索引
  • nginx部署成功却没有办法访问
  • 进程管理
  • S:List
  • CLOUD常用表
  • 【题解】Luogu P2766 最长不下降子序列问题
  • FBO中多重采样抗锯齿(MSAA MultiSampling Anti-Aliasing)
  • 时间复杂度分析经典问题——最大子序列和
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • HTTP--网络协议分层,http历史(二)
  • IDEA常用插件整理
  • iOS小技巧之UIImagePickerController实现头像选择
  • leetcode386. Lexicographical Numbers
  • LintCode 31. partitionArray 数组划分
  • Lsb图片隐写
  • MySQL-事务管理(基础)
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • Redis在Web项目中的应用与实践
  • Redis字符串类型内部编码剖析
  • XML已死 ?
  • 工作手记之html2canvas使用概述
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 智能合约Solidity教程-事件和日志(一)
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • 通过调用文摘列表API获取文摘
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (6)添加vue-cookie
  • (day 12)JavaScript学习笔记(数组3)
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • (转)重识new
  • .apk文件,IIS不支持下载解决
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET 8.0 中有哪些新的变化?
  • .net 验证控件和javaScript的冲突问题
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • .netcore如何运行环境安装到Linux服务器