首页 > 其他分享 >[NOI2018] 你的名字

[NOI2018] 你的名字

时间:2023-08-06 22:58:15浏览次数:37  
标签:md int 名字 num 命名 NOI2018 hd nw

题目描述

小 A 被选为了 ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了。

由于 ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手册规定:每年由命题委员会规定一个小写字母字符串,我们称之为那一年的命名串,要求每道题的名字必须是那一年的命名串的一个非空连续子串,且不能和前一年的任何一道题目的名字相同。

由于一些特殊的原因,小 A 不知道 ION2017 每道题的名字,但是他通过一些特殊手段得到了 ION2017 的命名串,现在小 A 有 \(Q\) 次询问:每次给定 ION2017 的命名串和 ION2018 的命名串,求有几种题目的命名,使得这个名字一定满足命题委员会的规定,即是 ION2018 的命名串的一个非空连续子串且一定不会和 ION2017 的任何一道题目的名字相同。

由于一些特殊原因,所有询问给出的 ION2017 的命名串都是某个串的连续子串,详细可见输入格式。

输入格式

第一行一个字符串 \(S\) ,之后询问给出的 ION2017 的命名串都是 \(S\) 的连续子串。
第二行一个正整数 \(Q\),表示询问次数。
接下来 \(Q\) 行,每行有一个字符串 \(T\) 和两个正整数\(l,r\),表示询问如果 ION2017 的命名串是 \(S_{l\ldots r}\),ION2018 的命名串是 \(T\) 的话,有几种命名方式一定满足规定。

对于所有数据,保证 \(1\leq l \leq r \leq |S|\),\(1\leq |T|,|S|\leq 5 \times 10^5,1\le q\le 10^5,\sum|T|\le 10^6\)

考虑全局询问怎么做,正难则反,考虑有多少个 \(S\) 和 \(T\) 的本质不同公共串。

考虑双指针,枚举 \(T\)的右端点,双指针维护左端点,在后缀自动机上的表现就是不断跳 fail 树上的父亲直到在 DAG 上有出边。这样子得到了 \(O(|T|)\) 个极大区间,那么在 \(T\) 的 fail 树上所有区间对应节点的父亲都是符合要求的。在上面搜一遍得到答案就可以了。

那么区间询问呢?可持久化线段树合并维护 endpos 集合,在维护双指针的时候判断一下到达的点在不在区间内就行了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long LL;
int x[N],y[N],p[N],m,n,q,len[N],cnt;
char str[N],t[N];
struct SAM{
	int tr[N*20],lc[N*20],rc[N*20],l[N],ch[N][26],fa[22][N],tme,idx=1,hd[N],ls=1,dfn[N],cnt,rt[N],dep[N],e_num,gl,gr;
	struct edge{
		int v,nxt;
	}e[N];
	void add_edge(int u,int v)
	{
		e[++e_num]=(edge){v,hd[u]};
		hd[u]=e_num;
	}
	void upd(int&o,int l,int r,int x)
	{
		if(!o)
			o=++tme;
		if(l==r)
			return tr[o]=l,void();
		int md=l+r>>1;
		if(md>=x)
			upd(lc[o],l,md,x);
		else
			upd(rc[o],md+1,r,x);
		tr[o]=max(tr[lc[o]],tr[rc[o]]);
	}
	int ask(int o,int l,int r,int y)
	{
		if(!o)
			return 0;
		if(r<=y)
			return tr[o];
		int md=l+r>>1,ret=0;
		if(md<y)
			ret=ask(rc[o],md+1,r,y);
		if(!ret)
			ret=ask(lc[o],l,md,y);
		return ret;
	}
	int merge(int x,int y)
	{
		if(!x||!y)
			return x|y;
		int p=++tme;
		lc[p]=merge(lc[x],lc[y]);
		rc[p]=merge(rc[x],rc[y]);
		tr[p]=max(tr[x],tr[y]);
		return p;
	}
	void dfs(int x)
	{
		dfn[x]=++cnt,dep[x]=dep[fa[0][x]]+1;
		for(int i=hd[x];i;i=e[i].nxt)
			dfs(e[i].v),rt[x]=merge(rt[x],rt[e[i].v]);
	}
	void init()
	{
		l[0]=-1;
		for(int i=1;i<=20;i++)
			for(int j=1;j<=idx;j++)
				fa[i][j]=fa[i-1][fa[i-1][j]];
		for(int i=1;i<=idx;i++)
			add_edge(fa[0][i],i);
		dfs(1);
	}
	void insert(int c)
	{
		int k=ls,p=++idx;
		l[p]=l[ls]+1;
		ls=p;
		upd(rt[p],1,n,l[p]);
		while(k&&!ch[k][c])
			ch[k][c]=p,k=fa[0][k];
		if(!k)
			fa[0][p]=1;
		else
		{
			int q=ch[k][c];
			if(l[k]==l[q]+1)
				fa[0][p]=q;
			else
			{
				int nw=++idx;
				l[nw]=l[k]+1,fa[0][nw]=fa[0][q];
				memcpy(ch[nw],ch[q],sizeof(ch[0]));
				fa[0][q]=fa[0][p]=nw;
				while(ch[k][c]==q)
					ch[k][c]=nw,k=fa[0][k];
			}
		}
	}
	void put(int o,int l,int r)
	{
		if(!o)
			return;
		if(l==r)
			printf("%d ",l);
		int md=l+r>>1;
		put(lc[o],l,md);
		put(rc[o],md+1,r);
	}
	void solve(int pl,int r)
	{
		int nw=1,gl=1,k=0;
		LL ans=0;
		for(int i=1;i<=m;i++)
		{
			while(nw)
			{
				int to=ch[nw][t[i]-'a'];
				if(to&&ask(rt[to],1,n,r)>=pl+l[fa[0][to]])
					break;
				nw=fa[0][nw];
			}
			int le=min(len[i-1]+1,l[nw]+1);
			nw=ch[nw][t[i]-'a']? ch[nw][t[i]-'a']:1;
			len[i]=min(le,ask(rt[nw],1,n,r)-pl+1);
		}
	}
}s;
int l[N],ch[N][26],fa[22][N],ls=1,idx=1,rr[N],hd[N],tg[N],e_num;
struct edge{
	int v,nxt;
}e[N];
void add_edge(int u,int v)
{
	e[++e_num]=(edge){v,hd[u]};
	hd[u]=e_num;
}
LL dfs(int x)
{
	LL ret=0;
	for(int i=hd[x];i;i=e[i].nxt)
	{
		ret+=dfs(e[i].v);
		if(tg[e[i].v])
		{
			tg[x]=l[x];
			ret+=tg[e[i].v]-l[x];
		}
	}
	return ret;
}
LL ask(int pl,int r)
{
	s.solve(pl,r);
	e_num=hd[1]=tg[1]=0;
	memset(ch[1],0,sizeof(ch[1]));
	ls=idx=1;
	for(int i=1;i<=m;i++)
	{
		int c=t[i]-'a',k=ls,p=++idx;
		memset(ch[p],0,sizeof(ch[p]));
		l[p]=l[k]+1,ls=p;
		tg[rr[i]=p]=hd[p]=0;
		while(k&&!ch[k][c])
			ch[k][c]=p,k=fa[0][k];
		if(!k)
			fa[0][p]=1;
		else
		{
			int q=ch[k][c];
			if(l[q]==l[k]+1)
				fa[0][p]=q;
			else
			{
				int nw=++idx;
				memcpy(ch[nw],ch[q],sizeof(ch[0]));
				tg[nw]=hd[nw]=0;
				fa[0][nw]=fa[0][q],l[nw]=l[k]+1;
				fa[0][p]=fa[0][q]=nw;
				while(ch[k][c]==q)
					ch[k][c]=nw,k=fa[0][k];
			}
		}
	}
	LL ans=0;
	for(int i=2;i<=idx;i++)
		ans+=l[i]-l[fa[0][i]],add_edge(fa[0][i],i);
	for(int i=1;i<=20;i++)
		for(int j=1;j<=idx;j++)
			fa[i][j]=fa[i-1][fa[i-1][j]];
	for(int i=1;i<=m;i++)
	{
		int k=rr[i];
		for(int j=20;~j;--j)
			if(fa[j][k]&&l[fa[j][k]]>=len[i])
				k=fa[j][k];
		tg[k]=len[i];
	}
	return ans-dfs(1);
}
int main()
{
	scanf("%s",str+1);
	n=strlen(str+1);
	for(int i=1;i<=n;i++)
		s.insert(str[i]-'a');
	s.init();
	scanf("%d",&q);
	while(q--)
	{
		int l,r;
		scanf("%s%d%d",t+1,&l,&r);
		m=strlen(t+1);
		printf("%lld\n",ask(l,r));
	}
}

标签:md,int,名字,num,命名,NOI2018,hd,nw
From: https://www.cnblogs.com/mekoszc/p/17610283.html

相关文章

  • 聊聊 RocketMQ 名字服务
    NameServer是专为RocketMQ设计的轻量级名字服务,它的源码非常精简,八个类,少于1000行代码。这篇文章,笔者会从基础概念、Broker发送心跳包、NameServer维护路由、ZookeepervsNameServer四个模块揭秘名字服务的设计精髓。1基础概念NameServer是一个非常简单的Topic路......
  • 六、通过ADB方式直接获取APP名字,非包名
    通过adb获取已安装的包名很简单 adbshellpmlistpackages这里可以获取到所有app的包名,包括系统的和自己安装的,但是想获取app名字,比如"微信",就很困难。搜集了一些方法:一、直接dumpsys网上较多流传的一个命令可以获取到APP的详细信息adbshelldumpsyspackagecom.tenc......
  • linux中如何修改网络命名空间中veth设备端点的名字?
    查看原有的设备名称为veth1  [root@centos7~]#ipnetnsexecns1iplink1:lo:<LOOPBACK>mtu65536qdiscnoopstateDOWNmodeDEFAULTgroupdefaultqlen1000link/loopback00:00:00:00:00:00brd00:00:00:00:00:005:veth1@if6:<BROADCAST,MULTIC......
  • java 获取随机名字
    Java获取随机名字的实现方法引言在Java开发过程中,有时候我们需要获取随机的名字,比如用于生成随机用户名、测试数据等。本文将介绍如何实现获取随机名字的功能,并给出具体的代码示例。实现步骤下面是获取随机名字的实现步骤,通过表格形式展示:步骤描述1.创建一个包含常......
  • java 地址截取域名字符串
    Java地址截取域名字符串在Java开发中,经常需要对URL进行处理,其中一个常见的需求是从完整的URL中提取出域名字符串。本文将介绍如何使用Java来截取域名字符串,并提供相关的代码示例。1.什么是域名?在互联网中,域名是用来标识互联网上的计算机或者网络服务的字符串。域名通常以点号......
  • mysql当一个字段以逗号隔开存多个名字,用sql取这个名字对应的id并修改
    当前有两个表,class班级表和student学生表  需求:我们需要把class班级表的student_ids中的name,改成student的id这里我们可以用“find_in_set”函数--注意s.name要在前面selectc.id,c.CLASS_NAME,GROUP_CONCAT(s.id)ascount,c.STUDENT_NAMESfromclasscleftjoinstu......
  • 吐槽一下依赖倒置这个糟糕的名字
    新手上来看见依赖倒置就迷糊,依赖这个词没问题,比如业务逻辑层依赖于数据库访问层。这没毛病。但是倒置这个词儿让人迷糊,这跟谁倒置了呀?把谁倒置了? 其实所谓的依赖倒置说的就是不管是业务逻辑层还是数据库访问层,都要面向接口编程。 我再翻译一下词啊,就是上面例子当中所谓的业......
  • python 输出list的名字
    Python输出list的名字作为一名经验丰富的开发者,我们来帮助一位刚入行的小白实现“Python输出list的名字”这个任务。下面是整个流程的步骤表格:步骤描述步骤1创建一个包含名字的list步骤2输出list的名字接下来,将逐步指导小白完成每个步骤,并提供相应的代码和注......
  • HJ45 名字的漂亮度
    1.题目读题HJ45 名字的漂亮度 考查点 2.解法思路 代码逻辑 具体实现首先,我们需要定义一个方法来计算一个字符串的漂亮度。漂亮度是指字符串中每个字母出现的次数乘以它在字母表中的位置,然后求和。例如,字符串"ABC"的漂亮度是11+22+3*3=14。我们可以用一......
  • .Net 根据类型全名字符串获取类型信息
    asp.net项目开发过程中用到了多个程序集(dll),如何根据类型全名(fullname)获取类型信息?如果项目(csproj)中设置了引用对应的dll或nupkg包,但是代码中没有任务地方引用改该程序集的类,则实际上运行时,该程序集不会被加载到进程中.假设有一个ThirdModels.dll,在该dll中定义命名......