首页 > 其他分享 >ABC 287 (E-Ex) 题解

ABC 287 (E-Ex) 题解

时间:2023-01-29 09:12:23浏览次数:35  
标签:ABC const int 题解 sze Ex ull times MOD

E

我的做法

对于每个串枚举他的答案,然后直接 hash 硬干就完了。

卡一卡就过去了

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
const int N = 5e5 + 10;
const ull P1 = 31;
const ull P2 = 29;
char s[N];
vector<char> t[N];
int n;
map<ull, int> st;

int main() {
	srand((unsigned) time(0));

	ull rd = 1ull * rand() * rand();
	ull bt = rand() % 4;

	scanf("%d", &n);
	for (int i = 1; i <= n; i ++) {
		scanf("%s", s + 1);
		int len = strlen(s + 1);
		ull hsh1 = 0, hsh2 = 0;
		for (int j = 1; j <= len; j ++) 
			t[i].push_back(s[j]), hsh1 = hsh1 * P1 + s[j] - 'a' + 1, hsh2 = hsh2 * P2 + s[j] - 'a' + 1, st[1ull * hsh1 * hsh2] ++;
	}
	for (int i = 1; i <= n; i ++) {
		int cnt = 0;
		bool flag = false;
		ull hsh1 = 0, hsh2 = 0;
		// printf("string: %d\n", i);
		for (char ch : t[i]) {
			hsh1 = hsh1 * P1 + ch - 'a' + 1, hsh2 = hsh2 * P2 + ch - 'a' + 1, st[1ull * hsh1 * hsh2] --;

			// printf("pos: %d map: %d hsh1: %llu hsh2: %llu tot hsh: %llu\n", cnt + 1, st[hsh1 ^ hsh2], hsh1, hsh2, hsh1 ^ hsh2);
			if (st[1ull * hsh1 * hsh2] <= 0) {
				flag = true;
				printf("%d\n", cnt);
				break;
			}

			++ cnt;
		}
		if (!flag) printf("%d\n", cnt);

		hsh1 = 0, hsh2 = 0;
		for (int j = 0; j <= cnt; j ++) hsh1 = hsh1 * P1 + t[i][j] - 'a' + 1, hsh2 = hsh2 * P2 + t[i][j] - 'a' + 1, st[1ull * hsh1 * hsh2] ++;
	}
	return 0;
}

官方做法

应该类似于建一个 trie 树,然后判断 size 是否大于 \(1\)。

F

我们可以考虑进行一个类似于树上背包的计数 dp。

设 \(f_{u,j,0/1}\) 表示在点 \(u\),当前有 \(j\) 个连通块,且 \(u\) 选/不选 的方案数。

转移我们分几类,记 \(v\) 是 \(u\) 的儿子。

  • \(f_{v,k,0} \times f_{u,j,0} -> f_{u,k+j,0}\)
  • \(f_{v,k,1} \times f_{u,j,0} -> f_{u,k+j,0}\)
  • \(f_{v,k,0} \times f_{u,j,1} -> f_{u,k+j,1}\)
  • \(f_{v,k,1} \times f_{u,j,1} -> f_{u,k+j-1,1}\)

然后就完事了,复杂度也是类似于树上背包的 \(\mathcal{O}(n^2)\)。

#include <bits/stdc++.h>
using namespace std;

const int N = 5010;
typedef long long ll;
const ll MOD = 998244353;
ll f[N][N][2];
int n, head[N], nxt[N * 2], to[N * 2], tot, sze[N];
void add(int x, int y) { nxt[++ tot] = head[x]; head[x] = tot; to[tot] = y; }

void dfs(int x, int fa) {
	sze[x] = 1;
	f[x][0][0] = 1;
	f[x][1][1] = 1;
	for (int i = head[x]; i; i = nxt[i]) {
		int y = to[i];
		if (y != fa) {
			dfs(y, x);

			for (int k = sze[x]; k >= 0; k --) {
				for (int j = sze[y]; j >= 1; j --) {
					f[x][k + j][0] = (f[x][k + j][0] + (f[y][j][0] + f[y][j][1]) % MOD * f[x][k][0] % MOD) % MOD;
					if (k) {
						f[x][k + j][1] = (f[x][k + j][1] + f[y][j][0] * f[x][k][1] % MOD) % MOD;
						if (j)	
							f[x][k + j - 1][1] = (f[x][k + j - 1][1] + f[y][j][1] * f[x][k][1] % MOD) % MOD;
					}
				}
			}

			sze[x] += sze[y];
		}
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 1, u, v; i < n; i ++) scanf("%d%d", &u, &v), add(u, v), add(v, u);
	dfs(1, 1);
	for (int i = 1; i <= n; i ++) printf("%lld\n", (f[1][i][0] + f[1][i][1]) % MOD);
	return 0;
}

G

首先我们将可能的牌的分数离散化。

然后我们考虑如何去处理询问。

我们每次二分一个最小的 \(l\) 使得 \([l,+ \infty)\) 内牌的数量小于限制。

因为是一个类似于前缀和的东西,我们可以将序列倒过来然后直接用树状数组加二分处理出来。

不难发现操作 \(1\) 和操作 \(2\) 也很好维护。

然后我们就可以愉快的解决这道题了。

感觉细节比较多,代码待会再补。

EX

考虑进行 bitset 优化的 floyed,我们记录起点和中间最大的转移位置,然后把终点压在一个 bitset 中,每次询问二分回答即可。

标签:ABC,const,int,题解,sze,Ex,ull,times,MOD
From: https://www.cnblogs.com/luanmenglei/p/17071699.html

相关文章