首页 > 其他分享 >题解:P3823 [NOI2017] 蚯蚓排队

题解:P3823 [NOI2017] 蚯蚓排队

时间:2025-01-21 11:42:30浏览次数:1  
标签:数字串 int 题解 NOI2017 P3823 哈希 x1 蚯蚓 op

题目链接

https://www.luogu.com.cn/problem/P3823

分析

先解决队伍的合并与分离问题。使用链表结构,分别维护每只蚯蚓的前驱和后继即可。

然后考虑如何统计答案。可以对每只蚯蚓的“向后 \(k\) 数字串”使用字符串哈希的方式获得哈希值,再用一个哈希表存储每个哈希值出现的次数。对于给定的数字串 \(s\) 的每个长度为 \(k\) 的连续子串 \(t\),\(f(t)\) 即为 \(t\) 的哈希值在哈希表中出现的次数。

最后考虑维护每只蚯蚓的“向后 \(k\) 数字串”。

  • 当 \(k=1\) 时,每只蚯蚓的“向后 \(k\) 数字串”是它的长度。
  • 当 \(k \ge 2\) 时,每只蚯蚓的“向后 \(k\) 数字串”只有经历蚯蚓队伍的合并才会形成,只有经历蚯蚓队伍的分离才会消失(即队伍合并是数字串产生的必要不充分条件,队伍分离是数字串消失的必要不充分条件)。具体地,当进行队伍的合并操作时,对于前方队伍中的任何一只蚯蚓,若它的“向后 \(k\) 数字串”跨越了两个队伍,则这条数字串因此次合并而产生,将这个数字串的哈希值在哈希表中的出现次数 \(+1\) 即可。队伍的分离操作同理。

细节内容见代码注释。

Code

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define i64 long long
#define uns unsigned

using namespace std;
using namespace __gnu_pbds;

constexpr int N=2e5+5;
int n,m;
char s[int(1e7)+5];

namespace Solve{
	//unordered_map 太慢,使用 pbds 的哈希表 
	gp_hash_table<uns i64,int>ght;
	
	//字符串哈希 
	constexpr uns i64 p=131;
	uns i64 pw[int(1e7+5)],hsh[int(1e7+5)];
	//预处理 
	void init_pw(int n){
		pw[0]=1;
		for(int i=1;i<=n;++i)pw[i]=pw[i-1]*p;
		return;
	}
	//对整个数字串进行哈希 
	void make_hash(int n,char *s,uns i64 *hash_table){
		for(int i=1;i<=n;++i)
			hash_table[i]=hash_table[i-1]*p+s[i]-48;
		return;
	}
	//获取子串的哈希值 
	uns i64 ask_hash(int l,int r,uns i64 *hash_table){
		if(l>r)return 0;
		return hash_table[r]-hash_table[l-1]*pw[r-l+1];
	}
	
	//链表 
	struct Node{
		int pre,nxt;//指针域,前驱和后继 
		int dat;//数据域,蚯蚓的长度 
	}L[N];
	//合并 
	void Union(int p1,int p2){
		//更新指针 
		L[p1].nxt=p2,L[p2].pre=p1;
		
		//由于题目中 k<=50,拥有跨越两个队伍的数字串的蚯蚓一定不超过 50 个 
		for(int i=p1,cnt_i=50;(~i)&&(cnt_i)!=0;i=L[i].pre,--cnt_i){
			uns i64 num_str=0ull;
			bool flg=false;
			//数字串长度不超过 50 
			for(int j=i,cnt_j=50;(~j)&&(cnt_j)!=0;j=L[j].nxt,--cnt_j){
				num_str=num_str*p+L[j].dat;
				
				if(j==p2)flg=true;//开始跨越两个队伍 
				if(flg)++ght[num_str];
			}
		}
		return;
	}
	//分离 
	void Cut(int p1){
		int p2=L[p1].nxt;
		
		for(int i=p1,cnt_i=50;(~i)&&(cnt_i)!=0;i=L[i].pre,--cnt_i){
			uns i64 num_str=0ull;
			bool flg=false;
			
			for(int j=i,cnt_j=50;(~j)&&(cnt_j)!=0;j=L[j].nxt,--cnt_j){
				num_str=num_str*p+L[j].dat;
				
				if(j==p2)flg=true;
				if(flg)--ght[num_str];
			}
		}
		
		//注意,由于要更新跨越两队伍的数字串的信息,指针的更新要放在后面 
		L[p1].nxt=L[p2].pre=-1;
		return;
	}
	
	//统计答案 
	constexpr i64 mod=998244353;
	i64 ask_ans(int k,char *s){
		int len=strlen(s+1);
		make_hash(len,s,hsh);
		
		i64 ans=1;
		for(int i=k;i<=len;++i){
			ans=ans*ght[ask_hash(i-k+1,i,hsh)]%mod;
			if(ans==0)break;
		}
		
		return ans;
	}
}

using namespace Solve;

int main(){
	ios::sync_with_stdio(false),
	cin.tie(nullptr),cout.tie(nullptr);
	
	init_pw(int(1e7));
	
	cin>>n>>m;
	
	//初始化 
	for(int i=1;i<=n;++i){
		cin>>L[i].dat;
		L[i].pre=L[i].nxt=-1,++ght[L[i].dat];
	}
	
	int op,x1,x2;
	for(int id_op=1;id_op<=m;++id_op){
		cin>>op;
		
		if(op==1){
			cin>>x1>>x2;
			Union(x1,x2);
		}
		else if(op==2){
			cin>>x1;
			Cut(x1);
		}
		else{
			cin>>(s+1)>>x1;
			cout<<ask_ans(x1,s)<<'\n';
		}
	}
	return 0;
}

标签:数字串,int,题解,NOI2017,P3823,哈希,x1,蚯蚓,op
From: https://www.cnblogs.com/ezhe/p/18683348

相关文章

  • 2024 (ICPC) Jiangxi Provincial Contest I 题 Neuvillette Circling题解
    简单思路一个圆套中了几个点,如果不断缩小这个圆,那么最终的结果有两种有两个点卡住了这个圆,且这两点一定是直径有三个或者三个以上的点卡住了这个圆,圆心在这三个点围成的三角形的外接圆圆心。因此我们枚举两点作为直径,或者枚举三个点作为圆的内接三角形,求这个三角形的外接圆......
  • 2024 (ICPC) Jiangxi Provincial Contest L 题 Campus 题解
    简单思路首先对于所有的出口求一遍最短路,由于出口只会打开并关闭一次,所以大门的开启状态是相当有限的(最多大概30种),我们对于每一种状态直接暴力求答案最后输出即可。复杂度大概\(O(knlogn)\)参考代码#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;type......
  • Codeforces Round 998 (Div. 3) 部分题解
    写题解的时候这场在评测,就不放代码了。E.GraphComposition题意给两个无向简单图,对图\(1\)添加若干条边或删除若干条边,使得两图的连通性一致,最少需要变更多少条边。题解求出图\(2\)的连通性,考虑图\(1\)的所有边,若违背了图\(2\)联通性的要删除(图\(2\)不联通但图\(......
  • 20250120 T2 simu题解
    简单模拟(simu)【题目描述】给定$a_1,a_2,\ldots,a_n$和$b_1,b_2,\ldots,b_n$。对于所有整数$x\in[l,r]$,模拟以下过程并求出变量$res$的最终的值。res=0fori=1ton:ifx>=a[i]:x=x-a[i]res=res+b[i]【输入格式】......
  • 「题解」二进制与一
    传送门:水滴、洛谷题目大意:给定一个正整数$n$,给出操作次数$p$。每次操作让$n$加上一个非负整数$x$,使得$n$的第$k$位为$0$(从右往左数)。如果本身为$1$就不用操作。且每次操作$n+x$回影响后续操作。问$x$的和是多少。首先我们要知道,判断$n$的第$k$位是否为$......
  • 题解:CF580B Kefa and Company
    CF580BKefaandCompany前言。其实本题与这道题极为相似,所以建议降橙。思路因为输入顺序不影响就结果,所以可以先给\(a\)按照工资从小到打排序一下序(这里\(a\)使用MAP)。然后再使用尺取法,只要\(a_{r+1}\)的值减\(a_l\)的值\(\ltk\)就将\(r\)加\(1\)。然后发现每......
  • [BZOJ3160] 万径人踪灭 题解
    首先正难则反,想到答案即为满足第一条要求的回文子序列数量,减去回文子串数量。回文子串数量\(hash+\)二分即可,考虑前半部分。假如我们将一个回文子序列一层层剥开,就会发现它其实是由多个相同的字母对拼成的。那么容易想到把字母\(a\)和字母\(b\)的贡献分开计算。那第一条要......
  • PKUWC 2025 题解
    本人太菜,实在不会T3,所以只有T1,T2的题解。注:考场上只做出来了Day1T1,其他题参考了其他人的题解。Day1T1电池检测题面有\(a\)个有电的电池和\(b\)个没电的电池,每次只能选择两个电池放进手电筒,只有这两个电池全有电才能让手电筒启动。问最坏情况下最少可以让手电筒启......
  • AT_abc389_f [ABC389F] Rated Range 题解
    题目传送门前置知识Treap|线段树解法考虑将询问的\(x\)离线下来在升序排序后一起处理。观察到每次操作只有\(+1\),即其之间的相对大小关系不会发生变化,此时就只需要支持将值在\([l,r]\)内的数加一,可以记录懒惰标记。线段树上二分找到端点或直接FHQ-Treap分裂出合法......
  • [ABC389C] Snake Queue题解
    前情题意:问题陈述有一个(蛇)队列。最初,队列是空的。你会得到\(Q\)个查询,这些查询应按给出的顺序处理。查询有三种类型:类型\(1\):以1l的形式给出。一条长度为\(l\)的蛇会被添加到队列的末尾。如果添加前队列为空,则新添加的蛇的头部位置为\(0\);否则,它就是队列中最后......