文章目录

  • 前言
  • 解析

前言

1000A快乐!!!awa
没有想象中的那么恶心。

解析

先考虑每次都询问 [1,n][1,n][1,n] 如何做。
正难则反,用T所有本质不同串数量减去是S串子串又是T的子串的数量
前者很好求,关键是后者
首先可以常规操作求出每个T的前缀在S上匹配的最长后缀长度 www
考虑对T也建出一个SAM,让T在自己的SAM上跑
假设跑到第 iii 个字符,处于结点 uuu
若 wi≤lenlinkuw_i\le len_{link_u}wi​≤lenlinku​​,说明在 uuu 结点的等价类集合中没有任何子串是S的子串,那么直接往 linklinklink 跳即可,不断上跳,直到不满足这个条件
然后,令 ansu←max⁡(ansu,wi)ans_u\gets \max(ans_u,w_i)ansu​←max(ansu​,wi​)。
最后匹配子串的总和就是 ∑(ansi−lenlinki)\sum (ans_i-len_{link_i})∑(ansi​−lenlinki​​)

考虑有区间限制 [l,r][l,r][l,r]
发现其实后面对于T的SAM的操作是不影响的,唯一的不同就是 www 不太好求了
考虑对S的SAM的每个结点建一棵线段树,存储 endpos⁡\operatorname{endpos}endpos 集合
dfs 一遍线段树合并就可以求出来
(注意这里需要保留原有的线段树,需要可持久化)
然后我们继续常规操作,在S的串上跑T试图求出 wiw_iwi​
但是现在的答案还要与 max⁡i∈endposu⁡,i≤ri−l+1\max_{i\in \operatorname{endpos_u},i\le r}i-l+1maxi∈endposu​,i≤r​i−l+1 取个 min
(也就是合法右端点左侧最大的endpos,可以通过刚才建出的线段树查询)

那么我们再匹配之余看看往父亲跳是否可以不劣,如果不劣就往上,这样就可以了

注意跳父亲的条件是不劣而不是更优,因为可能需要跳多次 linklinklink 才能跳到最优解,在此之前结果不变

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=1e6+100;
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}int m;#define mid ((l+r)>>1)
struct tree{int ls,rs,mx;
};
struct Segment_tree{tree tr[N<<5];int rt[N],tot;inline int copy(int x){tr[++tot]=tr[x];return tot;}inline void pushup(int k){tr[k].mx=max(tr[tr[k].ls].mx,tr[tr[k].rs].mx);return;}void upd(int &k,int l,int r,int p){if(!k) k=copy(0);if(l==r){tr[k].mx=l;return;}if(p<=mid) upd(tr[k].ls,l,mid,p);else upd(tr[k].rs,mid+1,r,p);pushup(k);}int merge(int x,int y,int l,int r){if(!x||!y) return x|y;if(l==r) return x;int now=copy(x);tr[now].ls=merge(tr[now].ls,tr[y].ls,l,mid);tr[now].rs=merge(tr[now].rs,tr[y].rs,mid+1,r);pushup(now);return now;}int ask(int k,int l,int r,int x,int y){if(!k) return 0;if(x<=l&&r<=y) return tr[k].mx;int res(0);if(x<=mid) res=max(res,ask(tr[k].ls,l,mid,x,y));if(y>mid) res=max(res,ask(tr[k].rs,mid+1,r,x,y));return res;}
}t;struct node{int len,fa;int tr[26];
};
struct SAM{char s[N];int n;node st[N];int cnt[N],id[N],pl[N];ll sum[N];int tot=1,lst=1;inline void clear(){while(tot){st[tot].len=st[tot].fa=0;memset(st[tot].tr,0,sizeof(st[tot].tr));sum[tot]=0;--tot;}tot=lst=1;return;}inline void ins(int c,int o){c-='a';int cur=++tot,p=lst;lst=tot;st[cur].len=st[p].len+1;pl[cur]=o;for(;p&&!st[p].tr[c];p=st[p].fa) st[p].tr[c]=cur;if(!st[p].tr[c]) st[cur].fa=1;else{int q=st[p].tr[c];if(st[q].len==st[p].len+1) st[cur].fa=q;else{int pp=++tot;st[pp]=st[q];st[pp].len=st[p].len+1;st[q].fa=st[cur].fa=pp;for(;p&&st[p].tr[c]==q;p=st[p].fa) st[p].tr[c]=pp;}}}void calc(){fill(cnt,cnt+1+n,0);for(int i=1;i<=tot;i++) ++cnt[st[i].len];for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];for(int i=tot;i>=1;i--) id[cnt[st[i].len]--]=i;for(int i=tot;i>=1;i--){if(id[i]!=1) sum[id[i]]=1;for(int j=0;j<26;j++) sum[id[i]]+=sum[st[id[i]].tr[j]];}return;}void build(){scanf(" %s",s+1);n=strlen(s+1);for(int i=1;i<=n;i++) ins(s[i],i);return;}void print(){for(int i=1;i<=tot;i++)printf("i=%d fa=%d len=%d sum=%lld pl=%d\n",i,st[i].fa,st[i].len,sum[i],pl[i]);}
}s1,s2;vector<int>v[N];
int ans[N];
void dfs(int x){if(s1.pl[x]){t.upd(t.rt[x],1,s1.n,s1.pl[x]);}for(int to:v[x]){dfs(to);t.rt[x]=t.merge(t.rt[x],t.rt[to],1,s1.n);}// printf("x=%d mx=%d\n",x,t.tr[t.rt[x]].mx);return;
}int L,R;
inline int lenth(int x,int val){return max(0,min(min(val,s1.st[x].len),t.ask(t.rt[x],1,s1.n,1,R)-L+1));
}
void work(){s2.build();s2.calc();L=read();R=read();// s2.print();int p1=1,p2=1,now(0);for(int i=1;i<=s2.n;i++){int c=s2.s[i]-'a';while(p1>1&&!s1.st[p1].tr[c]) p1=s1.st[p1].fa,now=s1.st[p1].len;if(s1.st[p1].tr[c]){p1=s1.st[p1].tr[c];++now;}//printf("  p1=%d now=%d\n",p1,now);while(p1>1&&lenth(s1.st[p1].fa,now)>=lenth(p1,now)){//printf("  jump: ask=%d mx=%d rt=%d (%d %d) (%d %d)\n",// t.ask(t.rt[p1],1,s1.n,1,R),t.tr[t.rt[p1]].mx,t.rt[p1],1,s1.n,1,R);p1=s1.st[p1].fa;}now=lenth(p1,now);p2=s2.st[p2].tr[c];while(p2>1&&s2.st[s2.st[p2].fa].len>=now) p2=s2.st[p2].fa;ans[p2]=max(ans[p2],now);//printf("i=%d p1=%d p2=%d now=%d\n",i,p1,p2,now);}for(int i=s2.tot;i>=1;i--){int f=s2.st[s2.id[i]].fa;ans[s2.st[s2.id[i]].fa]=max(ans[f],min(s2.st[f].len,ans[s2.id[i]]));}ll res=s2.sum[1];//printf("tot=%lld\n",res);for(int i=2;i<=s2.tot;i++){//printf("i=%d ans=%d\n",i,ans[i]);res-=max(0,ans[i]-s2.st[s2.st[i].fa].len);ans[i]=0;}printf("%lld\n",res);s2.clear();
}signed main(){#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifs1.build();//s1.print();for(int i=1;i<=s1.tot;i++) v[s1.st[i].fa].push_back(i);dfs(1);m=read();while(m--) work();return 0;
}
/*
*/

P4770:你的名字(SAM、线段树合并)相关推荐

  1. P4770-[NOI2018]你的名字【SAM,线段树合并】

    正题 题目链接:https://www.luogu.com.cn/problem/P4770 题目大意 给出一个长度为nnn的字符串SSS.qqq次询问给出一个串TTT和一个区间[L,R][L,R][ ...

  2. CF1037H Security——SAM+线段树合并

    又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...

  3. CF700E-Cool Slogans【SAM,线段树合并,dp】

    正题 题目链接:https://www.luogu.com.cn/problem/CF700E 题目大意 给出一个字符串SSS,求一个最大的kkk使得存在kkk个字符串其中s1s_1s1​是SSS的子 ...

  4. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree...

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  5. 2021CCPC华为云挑战赛:HDU 7091 重叠的子串(SAM + 线段树合并)

    重叠的子串 给定一个长度为n(1≤∣s∣≤105)n(1 \le \mid s \mid \le 10 ^ 5)n(1≤∣s∣≤105)的只由小写字母构成的字符串sss,有m,(1≤m≤106)m, ...

  6. CF666E-Forensic Examination【广义SAM,线段树合并】

    正题 题目链接:https://www.luogu.com.cn/problem/CF666E 解题思路 给出一个串SSS和nnn个串TiT_iTi​.mmm次询问Sa∼bS_{a\sim b}Sa∼ ...

  7. YbtOJ#532-往事之树【广义SAM,线段树合并】

    正题 题目链接:https://www.ybtoj.com.cn/problem/532 题目大意 给出nnn个点的一个TrieTrieTrie树,定义SxS_xSx​表示节点xxx代表的字符串 求m ...

  8. CF204E-Little Elephant and Strings【广义SAM,线段树合并】

    正题 题目链接:https://www.luogu.com.cn/problem/CF204E 题目大意 nnn个字符串的一个字符串集合,对于每个字符串求有多少个子串是这个字符串集合中至少kkk个字符 ...

  9. UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)

    NOI2019考前做NOI2018题.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417 (luogu) http ...

最新文章

  1. iOS 9应用开发教程之ios9的视图
  2. Silverlight 5 深入理解 - TechEd2011葡萄城讲师课程
  3. Firefox联手Chrome合作开发网页VR标准
  4. 一种创建进程间COM来启动IE的方式
  5. linux 关机时卸载sd,Linux下U盘SD卡的自动挂载和卸载
  6. SDCC 2016数据库峰会(深圳站)学习笔记
  7. PHP个人博客项目------切切歆语博客
  8. 【转】.NET Core 可移植类库PCL Portable Class Library详解
  9. 动态在网络图片上写字
  10. 曲线拟合最小二乘法对数c语言实现,基于最小二乘法的曲线拟合
  11. 计算机科学与技术哪些专业课,计算机科学与技术专业课程有哪些 计算机科学与技术有哪些科目...
  12. 第1节 中华人民共和国网络安全法
  13. hp打印机计算机接口,老司机操作电脑连接惠普打印机提示无法识别UsB端口的办法?...
  14. 快速关闭SELinux
  15. 【看表情包学Linux】缓冲区的概念 | Git 三板斧 | 实现简易进度条
  16. 公司章程违反了公司法该怎么办
  17. 查看电脑可支持最大内存容量的方法
  18. JavaWeb Ajax的使用
  19. oracle erp 库存账龄,系统管理、年结后,新年度做账龄分析,原来几年的账龄-用友U8...
  20. 程序员惨遭996,注册Github域名炮轰996工作模式

热门文章

  1. 程序员江湖鄙视链大全,看看你处于链条的哪一级?
  2. 大数据|意不意外?今年卖得最好的月饼是这个馅的……
  3. 桩筏有限元中的弹性板计算_永清县打桩机租赁钢板桩租赁怎么联系?
  4. mybatis-plus 会自动增加 order by_python自动撸支付宝基金答题红包
  5. javadoc文档的生成方法_[springboot 开发单体web shop] 4. Swagger生成Javadoc
  6. 华为交换机linux版本号,Cisco和华为交换机常用配置命令总结
  7. 支持向量回归代码_RDKit:基于支持向量回归(SVR)预测logP
  8. java 进程同步代码_java 实现进程间的同步(源代码)
  9. python choice添加下拉框_自定义Django Form中choicefield下拉菜单选取数据库内容实例...
  10. python字符串设置字体_python怎么更改字符串后几位