//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2023.7.30 DAY7解题报告

2023.7.30 DAY7解题报告

T1

暴力:直接模拟。

我们把每一个操作都模拟一下,我一开始用的 string,但是好像有长度上限,超了就炸了,改成了 char 数组,应该是慢了不少。

50pts;

#include <bits/stdc++.h>

#define int long long
#define N 10001000

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int n, m, k, L[N], R[N], maxn, C[N];
char s[N], ss[N];

signed main()
{
	n = read(), m = read(), k = read();
	for(int i = 1; i <= n; i ++)
		cin >> s[i];
	maxn = k;
	for(int i = 1; i <= m; i ++)
	{
		L[i] = read(), R[i] = read(), C[i] = read();
		maxn = max(maxn, R[i]);
	}
	if(n > 1000 || m > 1000 || k > 1000)
	{
		cout << s[1] << endl;
		return 0;
	}
	int len = n;
	while(len < maxn)
	{
		for(int i = len + 1; i <= len + n; i ++)
			s[i] = s[i - len];
		len += n;
	}
	n = len;
	for(int i = 1; i <= m; i ++)
	{
		int cnt = 0;
		int l = L[i], r = R[i], c = C[i];
		for(int j = 1; j <= c; j ++) ss[++ cnt] = s[j];
		for(int j = l; j <= r; j ++) ss[++ cnt] = s[j];
		for(int j = c + 1; j <= n; j ++) ss[++ cnt] = s[j];
		n = cnt;
		for(int i = 1; i <= n; i ++) s[i] = ss[i];
	}
	cout << s[k] << endl;
	return 0;
}

我们模拟一下,在这过程中最后怎么得到 \(k\) 的。

我们把询问一个一个倒回去,分下面三种情况:

  1. 当前的 \(k\) 小于 \(c_{i}\),那么挪过来的区间对他没有影响,我们可以不管。

  2. 当前的 \(k\)\(c_{i}\sim c_{i} + len\) 之间,那么我们的 \(k\) 在之前转移过来的区间内,也就是 \(l_{i} - 1 + (k-c_{i})\)

  3. 如果要是 \(k\)\(c_{i} + len\) 之外,那么我们就把 \(k\) 往左挪 \(len\) 个单位就好。

code:

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

int n, m, k, l[N], r[N], c[N];
char s[N];

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

signed main()
{
	n = read(), m = read(), k = read();
	for(int i = 1; i <= n; i ++)
		cin >> s[i];
	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;
		if(c[i] + len < k) k -= len;
		else if(c[i] < k && k <= c[i] + len) k = l[i] - 1 + (k - c[i]);
		else if(k >= c[i]) k = k;
//		cout << k << endl;
	}
	k %= n;
	cout << s[k] << endl;
	return 0;
}

T2

想到 KMP 了,但是没想到如何处理字符串。

一开始只想着怎么把 KMP 的过程改一下,但是正解是把原串处理一下跑 KMP。

考虑如何判断两个串相等?

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

两个字符串相等当且仅当新的数组相同

利用此性质做 KMP

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

map<char, int> mp;
int n, a[N], nxt[N], ans;
char s[N];

signed main()
{
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for(int i = 1; i <= n; i ++)
	{
		a[i] = i - mp[s[i]];
		mp[s[i]] = i;
	}
	int j = 0;
	for(int i = 2; i <= n; i ++)
	{
		while(j && min(a[i], j + 1) != a[j + 1]) j = nxt[j];
		if(min(a[i], j + 1) == a[j + 1]) j ++;
		nxt[i] = j;
	}
	for(int i = 2; i <= n; i ++)
		ans += nxt[i];
	cout << ans << endl;
	return 0;
}

T3

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

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

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

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

#include <bits/stdc++.h>

#define int unsigned long long
#define M 10000000
#define N 10000100
#define base 131

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int n, m, k, a[N], b[N], cnt[N], pm[N], now, ans, vis[N];
unordered_map<int, int> S;

signed main()
{
	n = read(), m = read();
	for(int i = 2; i <= M; i ++)
	{
		if(!vis[i]) pm[i] = ++ k, a[k] = i;
		for(int j = 1; j <= k && i * a[j] <= M; j ++)
		{
			vis[i * a[j]] = 1, pm[i * a[j]] = j;
			if (i % a[j] == 0) break;
		}
	}
	b[0] = 1, ++ S[0];
	for(int i = 1; i <= k; i ++) b[i] = b[i - 1] * base;
	for(int i = 1; i <= n; i ++)
	{
		int x = read();
		while(x != 1)
		{
			int y = pm[x], z = 0;
			while(x % a[y] == 0) x /= a[y], ++ z;
			now += - cnt[y] * b[y];
			cnt[y] = (cnt[y] + z) % m;
			now += cnt[y] * b[y];
		}
		ans += S[now] ++;
	}
	cout << ans << endl;
	return 0;
}

T4

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

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

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

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

没改完,std:

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
template <typename T>
void read(T &x) {
	x = 0; char c = getchar(); int f = 0;
	for (; !isdigit(c); c = getchar())
		f |= c == '-';
	for (; isdigit(c); c = getchar())
		x = x * 10 + (c ^ '0');
	if (f) x = -x;
}

template <typename T>
void write(T x, char ed = '\n') {
	if (x < 0) putchar('-'), x = -x;
	static short st[30], tp;
	do st[++tp] = x % 10, x /= 10; while (x);
	while (tp) putchar(st[tp--] | '0');
	putchar(ed);
}

#include <algorithm>
const int N = 10005001, M = 100050;
int ch[N][2], siz[N], f[N], ed[M], n, tot = 1;
ll v[M];
char s[M];

int insert(char *s) {
	int len = strlen(s + 1), p = 1;
	for (int i = 1;i <= len; ++i) {
		int c = s[i] - '0';
		if (!ch[p][c]) ch[p][c] = ++tot, f[tot] = p;
		p = ch[p][c], ++siz[p];
	}
	return p;
}
#include <assert.h>
int main() {
//	freopen ("13.in","r",stdin); 
	read(n);
	for (int i = 1;i <= n; ++i) {
		scanf ("%s", s + 1), read(v[i]);
		v[i] = v[i] * 2, ed[i] = insert(s);
	}
	for (int i = 1;i <= n; ++i) {
		int p = ed[i];
		while (p) v[i] += siz[p] - 1, p = f[p];
	}
	sort(v + 1, v + n + 1, [&](int x, int y) { return x > y;});
	ll ans = 0;
	for (int i = 1;i <= n; ++i) 
		i & 1 ? ans += v[i] : ans -= v[i];
	write(ans / 2);
	assert(tot < N);
	assert(ans % 2 == 0);
	return 0;
}

.

posted @ 2023-07-30 22:22  北烛青澜  阅读(7)  评论(0)    收藏  举报