Day7

发布时间 2023-07-30 20:44:19作者: Qinzh

Day6

暴力赛

T1

倒序考虑

若在复制位置的前面,则此次无效

在里面,则相应地变换

在后面, 则减去复制的长度

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define gt getchar
using namespace std;
inline ll read(){
	ll x=0,f=1;char ch=gt();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=gt();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gt();}
	return x*f;}
const int N = 1010500;
ll n, k, m, l[N], r[N], c[N];
char s[N];
int main() 
{
	n = read(), m = read(), k = read();
	cin >> s;
	for (int i = 1;i <= m; ++ i)
		l[i] = read(), r[i] = read(), c[i] = read();
	for (int i = m;i >= 1; -- i)
	{
		int len = r[i] - l[i] + 1, L = c[i] + 1, R = c[i] + len;
		if (L <= k && k <= R) k += l[i] - L;
		else if (k > R) k -= len;
	}
	cout << s[(k - 1) % n] << '\n';
	return 0;
}

T2

考虑两个串的比较

记录每个字符和这个字符上次出现的距离,变为新的数组

两串相等是新数组相等的充要条件

KMP

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define gt getchar
using namespace std;
inline ll read(){
	ll x=0,f=1;char ch=gt();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=gt();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gt();}
	return x*f;}
const int N = 1000500;
char s[N];
int f[N], nxt[N], T[31], n;
ll ans;
void KMP()
{
	int j = 0;
	for(int i = 2;i <= n; ++ i)
	{
		while(j && f[j + 1] != min(f[i], j + 1))j = nxt[j];
		if(f[j + 1] == min(f[i], j + 1)) ++ j;
		nxt[i] = j, ans += j;
	}
}

int main()
{
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for (int i = 1;i <= n; ++ i) 
		f[i] = i - T[s[i] - 'a'], T[s[i] - 'a'] = i;
	KMP();
	cout << ans << '\n';
    return 0;
}

T3

考虑一段区间可以开 \(k\) 次根号当且仅当区间乘起来以后每个质因子的幂次都是 \(k\) 的倍数。

维护一个串,第 \(i\) 个串为表示第 \(i\) 个质数的幂次模 \(k\) 的余数,当这个串全 \(0\) 时即为可以开 \(k\) 次根号。

考虑哈希维护这个串,用 map 存一下左端点,在右端点处询问即可。

注意用自然溢出而不是普通哈希。

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define gt getchar
using namespace std;
inline ll read(){
	ll x=0,f=1;char ch=gt();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=gt();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gt();}
	return x*f;}
const int P = 1e9 + 9;
const int N = 10050000;
int e[N], pr[N], a[N], num, v[N], n, k;
ll mi[N / 10];
void work(int n = 1e7)
{
	mi[0] = m1[0] = 1;
	for (int i = 2;i <= n; ++ i)
	{
		if (!e[i]) pr[e[i] = ++num] = i;
		for (int j = 1;j <= e[i] && pr[j] * i <= n; ++ j)
			e[pr[j] * i] = j;
	}
	for (int i = 1;i <= num; ++ i)
	{
		mi[i] = mi[i - 1] * 27ull;
		m1[i] = m1[i - 1] * 137ll % P;
	}
}
map<pair<ll, int> , int> mp;
int main()
{
	n = read(), k = read();
	work();
	ll all = 0, ans = 0, a2 = 0;
	mp[{all, a2}] = 1;
	for (int i = 1;i <= n; ++ i)
	{
		int x = read()
		while(x != 1)
		{
			int t = e[x];
			all = (all + mi[t]);
			a2 = (a2 + m1[t]) % P;
			if (++ v[t] == k)
			{
				v[t] = 0, all = (all - mi[t] * k);
				a2 = (a2 - 1ll * m1[t] * k % P + P) % P;
			}
			x /= pr[t];
		}
		ans += mp[{all, a2}] ++;
	}
	cout << ans << '\n';
	return 0;
}

T4

考虑题目所求为两者权值之差,考虑令串 \(s_i\) 的权值为 \(v_i + \frac{1}{2}\sum_{j}LCP(s_i, s_j)\)

那么如果两个串都被一个人选中,LCP 恰好被加了两遍

如果分别被两个人选中,一相减恰好消除。

所以直接对这个权值排序,从大到小以此选择即可。

讲课

KMP

与T2有关

考虑一个短串和比它长1的长串

长串的border可以由短串更新

P2375 [NOI2014] 动物园

给出一个字符串 ,求 \(\sum(num_i + 1)\)

对于字符串 的前 个字符构成的子串,既是其前缀又是其后缀,同时满足该前缀和后缀不重叠,这种字符串的数量记作 \(num_i\)

正常做一遍 KMP,再做一遍求答案,这一次维护指针 \(j\),当 \(j\) 大于 \(i\) 的一半时额外跳一次 \(next\)

P3193 [HNOI2008] GT考试

题面

阿申准备报名参加 GT 考试,准考证号为 \(N\) 位数\(X_1,X_2…X_n\ (0\le X_i\le 9)\),他不希望准考证号上出现不吉利的数字。
他的不吉利数字\(A_1,A_2,\cdots, A_m\ (0\le A_i\le 9)\)\(M\) 位,不出现是指 \(X_1,X_2\cdots X_n\) 中没有恰好一段等于 \(A_1,A_2,\cdots ,A_m\)\(A_1\)\(X_1\) 可以为 \(0\)

阿申想知道不出现不吉利数字的号码有多少种,输出模 \(K\) 取余的结果。

in

4 3 100
111

out

81

对于全部数据,\(N\leq10^9\)\(M\leq 20\)\(K\leq1000\)

solution

\(dp[i][j]\) 表示前 \(i\) 位数,匹配了 \(A\)\(j\) 位的方案数。

枚举下一位填什么,\(dp[i][ch[j][c]] += dp[i - 1][j], ch[j][c]\)表示当前匹配 \(A\) 了的前 \(j\) 位,再加一个字符 \(c\) 会匹配多少位。

发现可以矩阵优化,时间复杂度 \(O(M^3\log N)\)

P4696 [CEOI2011]Matching

对于整数序列 \((a_1,a_2,\cdots,a_n)\)\(1\sim n\) 的排列 \((p_1,p_2,\cdots,p_n)\),称 \((a_1,a_2,\cdots,a_n)\) 符合 \((p_1,p_2,\cdots,p_n)\),当且仅当:

  • \(\{a\}\) 中任意两个数字互不相同;
  • \((a_1,a_2,\cdots,a_n)\) 从小到大排序后,将会得到 \((a_{p_1},a_{p_2},\cdots,a_{p_n})\)

现在给出 \(1\sim n\) 的排列 \(\{p\}\) 和序列 \(h_1,h_2,\cdots,h_m\),请你求出哪些 \(\{h\}\) 的子串符合排列 \(\{p\}\)

in

5 10
2 1 5 3 4
5 6 3 8 12 7 1 10 11 9

out

2
2 6

对于 \(100\%\) 的数据,有 \(2\le n\le m\le 1\ 000\ 000;1\le h_i\le 10^9;1\le p_i\le n\),保证 \(\{h\}\) 中的元素互不相同,且 \(\{p\}\) 是一个排列。

在这题当中,首先将 由记录第 k 大对应什么位置变成第 k 个位置是第几大。

题目条件等价于将 的子串 离散化之后和 完全相同。

例如 \(s = \{21,45,23,7,8,4,43\} = \{4,7,5,2,3,1,6\}\)

考虑匹配跳 nxt 的时候,这个排列是变了的,但是如果最后一个字符在之前的串中的排名相同,那么这个字符就是可以

加入末尾的,即使其他数的排名变化了也是依然是相等的。

所以现在我们只需要快速判断一个数的排名,可以先预处理出 p 这个排列中每个数的前驱和后继,

注意这里的前驱(后继)是位置在自己前面的前驱和后继。

那么在 h 这个串上匹配的时候,只用查看对应 p 上的前驱后继是不是真正的前驱后继即可。

P5537 【XR-3】系统设计

小 X 需要你设计一个系统。

这个系统首先需要输入一棵 \(n\) 个点的有根树和一个长度为 \(m\) 的序列 \(a\),接下来需要实现 \(q\) 个操作。

操作分两种:

  1. 1 x l r 表示设定起点为有根树的节点 \(x\),接下来依次遍历 \(l \sim r\)。当遍历到 \(i\) 时,从当前节点走向它的编号第 \(a_i\) 小的儿子。如果某一时刻当前节点的儿子个数小于 \(a_i\),或者已经遍历完 \(l \sim r\),则在这个点停住,并输出这个点的编号,同时停止遍历。
  2. 2 t k 表示将序列中第 \(t\) 个数 \(a_t\) 修改为 \(k\)
  • \(1 \le n,m,q \le 5 \times 10 ^ 5\)
  • \(1 \le a_i \le n\)
  • 对于操作 \(1\),保证 \(1 \le x \le n\)\(1 \le l \le r \le m\)

考虑到每个点从根节点到其的路径是唯一的,设为\(p_x\),可以将操作 \(1\) 转化为从根节点开始,在操作区间前加入一段序列。

考虑中间经过的某个节点 \(x\),那么 \(p_x\) 一定为操作区间的一段前缀,二分 + 哈希即可。

加速:用线段树维护并在线段树上二分,时间复杂度 \(O(q\log n)\)

Manacher(马拉车)算法

维护每个位置回文半径

[HDU5371] Hotaru's problem

给定长度为 \(n\) 的数列 \(a[i]\) ,现要求找出最长的形似 \(s + s^R + s\) 的子串, \(s^R\)\(s\) 的反串。

\(N \le 10^5, 1 \le a[i] \le 10^9\)

回文半径,考虑一个 \(ss^R,s\) 有两个回文串构成,对应两个回文半径 \(s_1, s_2\),保证 \(s_1 + r_1 \ge s_2, s_2 - r_2 \le s_1\) 即可。

所以枚举 \(s_1\),求 \([s_1+1,s_1+r_1]\)这个区间 \(s_2 - r_2\) 的最小值即可。

P6216 回文匹配

对于一对字符串 \((s_1,s_2)\),若 \(s_1\) 的长度为奇数的子串 \((l,r)\) 满足 \((l,r)\) 是回文的,那么 \(s_1\) 的“分数”会增加 \(s_2\)\((l,r)\) 中出现的次数。

现在给出一对 \((s_1,s_2)\),请计算出 \(s_1\) 的“分数”。

答案对 \(2 ^ {32}\) 取模。 \(1 \le n, m \le 3 \times 10^6\)

考虑求回文半径,用 KMP 记录每个位置是否匹配

统计答案发现是等差数列,利用性质记录前缀和 \(\sum a_i \times i\)\(\sum a_i\)

P5446 [THUPC2018] 绿绿和串串

定义一种字符串操作翻转,即将字符串 \(S\) 的前 \(|S| - 1\)位复制一份,并翻转,然后放到字符串的末尾,如 abc 翻转后得到 abcba

现在给定一个字符串 \(S\),问有多少种长度的初始字符串 \(R\),使得 \(|S|\)\(|R|\) 翻转若干次后的前缀

显然,当 \(|R| > |S|\) 时一定存在答案,所以只要求出 \(|R| \le |S|\) 的方案.

从只翻转一次入手,发现满足 \(i + r[i]\) 的位置满足条件。

那么更多次是类似的,当且仅当 \(i +r[i]\) 满足条件且 \(r[i]\) 能覆盖 \(1\)\(i\).

Trie

Trie 树的基本操作:

插入,查询

01-Trie 的基本操作:

异或最大

P3065 [USACO12DEC] First! G

字典序相关很容易想到 Trie 树

考虑一个字符串什么时候最小,在 Trie 树上每次都走的最左边!

可以得到当前字母是所有出边中最小的,当前字母和其他字母连有向边表示字典关系,

最后拓扑排序即可。

时间复杂度 \(O(26m)\)