蚯蚓排队(码)
蚯蚓排队 解题报告
·题面
蚯蚓幼儿园有 \(n\) 只蚯蚓。幼儿园园长神刀手为了管理方便,时常让这些蚯蚓们列队表演。
所有蚯蚓用从 \(1\) 到 \(n\) 的连续正整数编号。每只蚯蚓的长度可以用一个正整数表示,根据入园要求,所有蚯蚓的长度都不超过 \(6\) 。神刀手希望这些蚯蚓排成若干个队伍,初始时,每只蚯蚓各自排成一个仅有一只蚯蚓的队伍,该蚯蚓既在队首,也在队尾。
神刀手将会依次进行 \(m\) 次操作,每个操作都是以下三种操作中的一种:
-
给出 \(i\) 和 \(j\) ,令 \(i\) 号蚯蚓与 \(j\) 号蚯蚓所在的两个队伍合并为一个队伍,具体来说,令 \(j\) 号蚯蚓紧挨在 \(i\) 号蚯蚓之后,其余蚯蚓保持队伍的前后关系不变。
-
给出 \(i\) ,令 \(i\) 号蚯蚓与紧挨其后的一只蚯蚓分离为两个队伍,具体来说,在分离之后, \(i\) 号蚯蚓在其中一个队伍的队尾,原本紧挨其后的那一只蚯蚓在另一个队伍的队首,其余蚯蚓保持队伍的前后关系不变。
-
给出一个正整数 \(k\) 和一个长度至少为 \(k\) 的数字串 \(s\) ,对于 \(s\) 的每个长度为 \(k\) 的连续子串 \(t\) (这样的子串共有 \(∣s∣−k+1\) 个,其中 \(∣s∣\) 为 \(s\) 的长度),定义函数 \(f(t)\),询问所有这些 \(f(t)\) 的乘积对 \(998244353\) 取模后的结果。其中 \(f(t)\) 的定义如下:
对于当前的蚯蚓队伍,定义某个蚯蚓的向后 \(k\) 数字串为:从该蚯蚓出发,沿队伍的向后方向,寻找最近的 \(k\) 只蚯蚓(包括其自身),将这些蚯蚓的长度视作字符连接而成的数字串;如果这样找到的蚯蚓不足 \(k\) 只,则其没有向后\(k\) 数字串。例如蚯蚓的队伍为 \(10\) 号蚯蚓在队首,其后是 \(22\) 号蚯蚓,其后是 \(3\) 号蚯蚓(为队尾),这些蚯蚓的长度分别为 \(4\) 、 \(5\) 、 \(6\) ,则 \(10\) 号蚯蚓的向后 \(3\) 数字串为 \(456\), \(22\) 号蚯蚓没有向后 \(3\) 数字串,但其向后 \(2\) 数字串为 \(56\) ,其向后 \(1\) 数字串为 \(5\) 。
而 \(f(t)\) 表示所有蚯蚓中,向后 \(k\) 数字串恰好为 \(t\) 的蚯蚓只数。
·\(Input\)
输入文件的第一行有两个正整数 \(n,m\) ,分别表示蚯蚓的只数与操作次数。
第二行包含 \(n\) 个不超过 \(6\) 的正整数,依次表示编号为 \(1,2,…,n\) 的蚯蚓的长度。
接下来 \(m\) 行,每行表示一个操作。每个操作的格式可以为:
1 $ i \ j $ $ (1≤i,j≤n)$ 表示:令 \(i\) 号与 \(j\) 号蚯蚓所在的两个队伍合并为一个队伍,新队伍中, \(j\) 号蚯蚓紧挨在 \(i\) 号蚯蚓之后。保证在此操作之前, \(i\) 号蚯蚓在某个队伍的队尾,\(j\) 号蚯蚓在某个队伍的队首,且两只蚯蚓不在同一个队伍中。
2 \(i\) \((1≤i≤n)\) 表示:令 \(i\) 号蚯蚓与紧挨其后一个蚯蚓分离为两个队伍。保证在此操作之前, \(i\) 号蚯蚓不是某个队伍的队尾。
3 \(s\) \(k\)(\(k\)为正整数,\(s\)为一个长度至少为\(k\)的数字串)表示:询问 \(s\) 的每个长度为 \(k\) 的子串 \(t\) 的 \(f(t)\) 的乘积,对 \(998244353\) 取模的结果。 \(f(t)\) 的定义见题目描述。
同一行输入的相邻两个元素之间,用恰好一个空格隔开。
输入文件可能较大,请不要使用过于缓慢的读入方式。
·Output
依次对于每个形如 $ \ 3 \ s \ k $ 的操作,输出一行,仅包含一个整数,表示询问的结果。
·样例输入
5 9
3 1 3 5 3
3 333135 2
3 333135 1
1 1 3
1 2 5
1 3 2
1 5 4
3 333135 2
3 333135 1
3 333135 3
·样例输出
0
81
1
81
0
·\(code\)
相信看码的你,可以看懂
#include<bits/stdc++.h>
#define int unsigned long long
#define together ios::sync_with_stdio( 0 ) ; cin.tie( 0 ) ; cout.tie( 0 )
using namespace std ;
//unordered_map<int,int>hasher;
const int down = 131 ;
const int Last_mod = 998244353 ;
const int N = 1e6 + 100 ;
const int mod = 10000019 ;
const int CuFeO4 = 2e7 + 20 ;
int does[ 60 ] , n , m , opt ;
int length[ N ] ; int Next_point[ N ] , Pre_point[ N ] ;
int Max_k = 50 ;
namespace HASH
{
struct Hash_Table_Of_List
{
struct One_node
{
int key_save ;
int value , next ;
} ;
One_node grass[ CuFeO4 << 1 ] ;
int head[ CuFeO4 << 1 ] , cnt ;
int Hash_return( int key )
{
return ( key % mod + mod ) % mod ;
}
int& operator[]( int key_in )
{
int inlist = Hash_return( key_in ) ; // 获取头指针
for (int i = head[ inlist ] ; i; i = grass[ i ].next )
if ( grass[ i ].key_save == key_in )
return grass[ i ].value ;
return grass[ ++ cnt ] = ( One_node ){ key_in, 0, head[ inlist ] } , head[ inlist ] = cnt , grass[ cnt ].value ;
}
Hash_Table_Of_List( )
{
cnt = 0;
memset( head , 0 , sizeof( head ) ) ;
}
}hasher;
inline void insert( )
{
int x , y ;
cin >> x >> y ;
Next_point[ x ] = y ;
Pre_point[ y ] = x ;
int omega = 0 ;
for( int i = x , len = 1 ; i > 0 && len < 50 ; ++ len , i = Pre_point[ i ] )
{
int Klen = len + 1 , pomega ;
pomega = omega = ( omega + ( does[ len - 1 ] * ( length[ i ] + '0' ) ) ) ;
// cout << ( length[ i ] + '0' ) << ' ' ;
// cout << omega << '\n' ;
for ( int j = y ; j > 0 && Klen <= 50 ; ++ Klen , j = Next_point[ j ] )
{
pomega = ( pomega * down + length[ j ] + '0' ) ;
hasher[ pomega ] = ( hasher[ pomega ] + 1 ) % Last_mod ;
// cout << length[ i ] <<' ' << i << ' ' << length[ j ] << ' ' << j << ' ' ;
// cout << pomega << '\n' ;
}
}
}
inline void cut_off( )
{
int x ;
cin >> x ;
int y = Next_point[ x ] ;
Pre_point[ Next_point[ x ] ] = 0 ;
Next_point[ x ] = 0 ;
int omega = 0 ;
for( int i = x , len = 1 ; i > 0 && len < 50 ; ++ len , i = Pre_point[ i ] )
{
int Klen = len + 1 , pomega ;
pomega = omega = ( omega + ( does[ len - 1 ] * ( length[ i ] + '0' ) ) ) ;
// cout << ( length[ i ] + '0' ) << ' ' ;
// cout << omega << '\n' ;
for ( int j = y ; j > 0 && Klen <= 50 ; ++ Klen , j = Next_point[ j ] )
{
pomega = ( pomega * down + length[ j ] + '0' ) ;
hasher[ pomega ] = ( hasher[ pomega ] - 1 ) % Last_mod ;
// cout << length[ i ] <<' ' << i << ' ' << length[ j ] << ' ' << j << ' ' ;
// cout << pomega << '\n' ;
}
}
}
inline void query( )
{
int k ; char s[ N ] ;
cin >> s + 1 >> k ;
int ans = 1 ;
int lens = strlen( s + 1 ) ;
int hash_s = 0 ;
for( int i = 1 ; i <= k ; ++ i )
{
//cout << does[ k - i ] * s[ i ] << '\n' ;
hash_s = ( hash_s + does[ k - i ] * s[ i ] ) ;
//cout << hash_s << '\n' ;
}
//cout << hash_s << endl ;
//cout << hasher[ hash_s ] << '\n' ;
ans = ( ans * ( hasher[ hash_s ] ) % Last_mod ) % Last_mod ;
//cout << ' ' << ans ;
//cout << ' ' << k + 1 << lens << '\n' ;
for( int i = k + 1 ; i <= lens ; ++ i )
{
int st = i - k ;
hash_s = ( hash_s - does[ k - 1 ] * s[ st ] ) ;
hash_s = ( ( hash_s * down ) + s[ i ] ) ;
//cout << hash_s << '\n' ;
ans = ( ans * ( hasher[ hash_s ] ) % Last_mod ) % Last_mod ;
}
cout << ans << '\n' ;
}
}
using namespace HASH ;
signed main( )
{
#ifndef ONLINE_JUDGE
freopen( "cjs.in" , "r" , stdin ) ;
freopen( "cjs.out" , "w" , stdout ) ;
#endif
together ;
does[ 0 ] = 1 ;
for( int i = 1 ; i <= 50 ; i ++ )
{
does[ i ] = ( does[ i - 1 ] * down ) ;
}
cin >> n >> m ;
for( int i = 1 ; i <= n ; ++ i )
{
cin >> length[ i ] ;
hasher[ length[ i ] + '0' ] = hasher[ length[ i ] + '0' ] + 1 ;
//cout << length[ i ] << ' ' << hasher[ length[ i ] + '0' ] << '\n' ;
}
//cout << hasher[ '1' ] << ' ' << '\n' ;
for( int i = 1 ; i <= m ; ++ i )
{
cin >> opt ;
switch( opt )
{
case 1 :
{
insert( ) ;
break ;
}
case 2 :
{
cut_off( ) ;
break ;
}
default :
{
query( ) ;
break ;
}
}
}
}