CF diary

似乎不只有cf

1334E. Divisor Paths

\(\texttt{Difficulty:2200}\)

题意

一个正整数 \(D(1\le D\le10^{15})\) 。由 \(D\) 建出一张无向图,每个节点都是 \(D\) 的约数, 存在边 \((u,v)(u>v)\) 当且仅当存在素数 \(p\) 使得 \(v\cdot p=u\) ,边权为 \(u,v\) 的约数个数的差,现在有 \(q(1\le q\le10^5)\) 次询问,每次询问 \(u\)\(v\) 的最短路数量,模 \(998244353\)

题解

\(u>v\) ,如果 \(v\)\(u\) 的约数,那么从 \(v\) 不断乘上一些素数走到 \(u\) 即是最短路,我们对 \(\frac{u}{v}\) 分解质因数求一个多重集的排列即可。如果 \(v\) 不是 \(u\) 的约数,可以发现最短路一定会经过 \(gcd(u,v)\) 中转,于是分别求出 \(u\)\(gcd(u,v)\)\(v\)\(gcd(u,v)\) 的最短路,相乘即可。由于所有数都是 \(D\) 的约数,他们的质因数分解可以在对 \(D\) 质因数分解后,逐个尝试 \(D\) 的质因数来求得。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 200010;

LL D, Q, fact[110], invfact[110];
LL qpow(LL a, LL x, LL m)
{
	LL ans = 1;
	while (x)
	{
		if (x & 1)
			ans = ans * a % m;
		x >>= 1;
		a = a * a % m;
	}

	return ans % m;
}

void fact_init(LL n, LL m)
{
	fact[0] = fact[1] = 1;
	for (LL i = 2; i <= n; i++)
		fact[i] = fact[i - 1] * i % m;
	invfact[n] = qpow(fact[n], m - 2, m);;
	for (LL i = n; i > 0; i--)
		invfact[i - 1] = invfact[i] * i % m;
}

map<LL, LL>mp;

LL gcd(LL a, LL b)
{
	if (b == 0)
		return a;
	return gcd(b, a % b);
}

LL calc(LL u, LL v)
{
	LL nd = u / v;
	vector<LL>tmp;
	int all = 0;
	for (auto& [x, y] : mp)
	{
		int cnt = 0;
		while (nd % x == 0)
			nd /= x, cnt++;
		if (cnt)
			tmp.push_back(cnt);
		all += cnt;
	}
	LL ans = fact[all];
	for (auto& c : tmp)
		ans = ans * invfact[c] % mod;
	return ans;
}

void solve()
{
	fact_init(100, mod);
	LL x = D;
	for (LL i = 2; i * i <= x; i++)
	{
		while (x % i == 0)
			mp[i]++, x /= i;
	}
	if (x != 1)
		mp[x]++;
	LL u, v;
	while (Q--)
	{
		cin >> u >> v;
		if (u == v)
		{
			cout << 1 << endl;
			continue;
		}
		if (u < v)
			swap(u, v);
		LL k = gcd(u, v);
		if (k == v)
			cout << calc(u, v) << endl;
		else
			cout << calc(u, k) * calc(v, k) % mod << endl;
	}
}

int main()
{
	IOS;
	cin >> D >> Q;
	solve();

	return 0;
}

1322B. Present

\(\texttt{Difficulty:2100}\)

题意

一个长为 \(n(1\le n\le4\cdot10^5)\) 的序列 \(a(1\le a_i\le 10^7) \) ,求对其每个二元组求和后全部异或起来的结果。

题解

考虑每一位对于答案的贡献,两个数相加时如果第 \(k\) 位为 \(1\) ,那么其 \(0\sim k\) 位组成的数相加的值 \(\in[2^k,2^{k+1})\cup[2^{k+1}+2^k,+\infty)\) 。我们对于每个 \(k\) ,将原序列取模取出 \(0\sim k\) 位的数字排序后双指针即可得到该位上 \(1\) 的个数,根据奇偶性来判断最后答案上该位的值即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 400010;

int N, A[maxn], B[maxn];

void solve()
{
	int ans = 0;
	for (int i = 0; i <= 25; i++)
	{
		for (int j = 1; j <= N; j++)
			B[j] = A[j] % (1 << (i + 1));
		sort(B + 1, B + N + 1);
		int cnt = 0;
		for (int l = 1, r = N; l <= N; l++)
		{
			while (r > l && B[l] + B[r] >= (1 << i))
				r--;
			if (r <= l)
				r = l;
			cnt += N - r;
		}
		for (int l = 1, r = N; l <= N; l++)
		{
			while (r > l && B[l] + B[r] >= (1 << (i + 1)))
				r--;
			if (r <= l)
				r = l;
			cnt -= N - r;
		}
		for (int l = 1, r = N; l <= N; l++)
		{
			while (r > l && B[l] + B[r] >= (1 << (i + 1)) + (1 << i))
				r--;
			if (r <= l)
				r = l;
			cnt += N - r;
		}
		if (cnt & 1)
			ans |= 1 << i;
	}
	cout << ans << endl;
}

int main()
{
	IOS;
	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> A[i];
	solve();

	return 0;
}

1495C. Garden of the Sun

\(\texttt{Difficulty:2300}\)

题意

一个 \(n\cdot m(1\le n\cdot m\le250000)\) 的网格,每个点.代表空地,X 代表太阳花,所有初始太阳花的格子之间没有公共边和点,要求种上任意数量的太阳花,使得所有太阳花四联通并且没有环。

题解

考虑由于初始的太阳花之间两两没有公共边和点,我们考虑每隔两行就将改行种满,这样太阳花形成了若干连通块,之后考虑将它们连接起来,中间间隔的两行找一个不会产生环的列,在该列上两个位置都种上花,对于边界处多出两行的情况,每列最后一行的位置上只要有花,就在倒数第二行该列也种上花即可,特判一下只有一列的请况。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define pb push_back
//#define int LL
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 510;

int T, N, M;
char A[maxn][maxn];

void solve()
{
	if (M == 1)
	{
		for (int i = 1; i <= N; i++)
			cout << 'X' << endl;
		return;
	}
	for (int i = 1; i <= N; i += 3)
	{
		for (int j = 1; j <= M; j++)
			A[i][j] = 'X';
	}
	for (int i = 2; i + 1 <= N; i += 3)
	{
		if (i + 1 == N)
		{
			for (int j = 1; j <= M; j++)
			{
				if (A[N][j] == 'X')
					A[N - 1][j] = A[N][j] = 'X';
			}
		}
		else
		{
			for (int j = 1; j <= M; j++)
			{
				if (j == 1)
				{
					if (A[i][j + 1] == '.' && A[i + 1][j + 1] == '.')
					{
						A[i][j] = A[i + 1][j] = 'X';
						break;
					}
				}
				else if (j == N)
				{
					if (A[i][j - 1] == '.' && A[i + 1][j - 1] == '.')
					{
						A[i][j] = A[i + 1][j] = 'X';
						break;
					}
				}
				else
				{
					if (A[i][j + 1] == '.' && A[i + 1][j + 1] == '.' && A[i][j - 1] == '.' && A[i + 1][j - 1] == '.')
					{
						A[i][j] = A[i + 1][j] = 'X';
						break;
					}
				}
			}
		}
	}
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= M; j++)
			cout << A[i][j];
		cout << endl;
	}
}

int main()
{
	IOS;
	cin >> T;
	while (T--)
	{
		cin >> N >> M;
		for (int i = 1; i <= N; i++)
		{
			for (int j = 1; j <= M; j++)
				cin >> A[i][j];
		}
		solve();
	}

	return 0;
}

1059D. Nature Reserve

\(\texttt{Difficulty:2200}\)

题意

\(n(1\le n\le 10^5)\) 个点,求包含所有点且与 \(x\) 轴相切的最小圆的半径,无解输出-1

题解

首先如果 \(x\) 轴两侧都有点或者其轴上有多于一个点则无解,对于有解的情况,为了便于处理,所有点都在 \(y\) 轴负方向时我们将它们都对称到正方向来。我们显然可以二分半径,对于每个半径 \(r\) ,圆心在 \(y=r\) 上。考虑如何 \(\texttt{check}\) ,对于每个点,圆心能包含它的一段横坐标是连续的,我们可以对每个点求出这段区间,然后取交集,如果不为空,则说明可行,否则不行,复杂度 \(O(nlogn)\) 。实现时注意精度,式子尽可能化简后再计算。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define pb push_back
//#define int LL
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;

int N;
PII P[maxn];

int cmp(double x)
{
	if (fabs(x) < eps)
		return 0;
	if (x < 0)
		return -1;
	return 1;
}

pair<LD, LD> calc(LD x1, LD y1, LD y)
{
	return make_pair(x1 - sqrt(2.0 * y1 * y - y1 * y1), x1 + sqrt(2.0 * y1 * y - y1 * y1));
}

bool check(LD y)
{
	LD l = -1e8, r = 1e8;
	for (int i = 1; i <= N; i++)
	{
		pair<LD, LD>seg = calc(P[i].first, P[i].second, y);
		if (cmp(seg.second - l) < 0 || cmp(seg.first - r) > 0)
			return false;
		l = max(l, seg.first), r = min(r, seg.second);
	}
	return true;
}

void solve()
{
	bool ne = false, po = false, zr = false;
	for (int i = 1; i <= N; i++)
	{
		if (zr && P[i].second == 0)
		{
			cout << -1 << endl;
			return;
		}
		if (P[i].second == 0)
			zr = true;
		else if (P[i].second > 0)
			po = true;
		else
			ne = true;
		if (ne && po)
		{
			cout << -1 << endl;
			return;
		}
	}
	for (int i = 1; i <= N; i++)
		P[i].second = abs(P[i].second);
	LD lo = 0, hi = 1e18;
	for (int i = 1; i <= 500; i++)
	{
		LD mid = (lo + hi) / 2;
		if (check(mid))
			hi = mid;
		else
			lo = mid;
	}
	cout << setiosflags(ios::fixed) << setprecision(12) << hi << endl;
}

int main()
{
	IOS;
	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> P[i].first >> P[i].second;
	solve();

	return 0;
}

P6146 [USACO20FEB]Help Yourself G

题意

\(n(1\le n\le 10^5)\) 个线段,其一个子集的复杂度为集合中线段取并后的连续段数量,求所有子集的复杂度之和。

题解

按左端点排序,记 \(f_i\) 为之在前 \(i\) 个线段中进行选取的复杂度之和,对于 \(f_{i+1}\) ,如果不考虑额外的贡献,第 \(i+1\) 条线段可选或不选,这部分为 \(f_i\cdot2\) ,对于额外的贡献,只有在已选的集合中没有右端点大于自己左端点的线段时才会产生,这部分记录一下这种线段的数量为 \(cntr\) ,则额外的贡献为 \(2^{cntr}\) ,递推即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;

int N;
PII S[maxn];
LL cntr[maxn * 2], f[maxn];

LL qpow(LL a, LL x)
{
	LL ans = 1;
	while (x)
	{
		if (x & 1)
			ans = ans * a % MOD;
		a = a * a % MOD, x >>= 1;
	}
	return ans;
}

void solve()
{
	sort(S + 1, S + N + 1);
	for (int i = 1; i <= N; i++)
		cntr[S[i].second]++;
	for (int i = 1; i <= N * 2; i++)
		cntr[i] += cntr[i - 1];
	for (int i = 1; i <= N; i++)
		f[i] = (f[i - 1] * 2LL % MOD + qpow(2LL, cntr[S[i].first - 1])) % MOD;
	cout << f[N] << endl;
}

int main()
{
	IOS;
	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> S[i].first >> S[i].second;
	solve();

	return 0;
}

1009F. Dominant Indices

\(\texttt{Difficulty:2300}\)

题意

一颗根为 \(1\)\(n(1\le n\le10^6)\) 的树,对于每个点 \(u\) ,求使 \(d(u,x)\) 最大的最小 \(x\)\(d(u,x)\)\(u\) 子树中距离 \(u\)\(x\) 点的数量。

题解

题目要求查询所有子树的信息,考虑 \(\texttt{dsu on tree}\) 。维护当前子树中每个深度的节点个数,同时维护最大值以及答案,这部分在累加时很好更新,清除轻子树贡献时也只需将最大值与答案都置为 \(0\) 即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 1000010;

int N, tot = 0, hson[maxn], siz[maxn], dep[maxn], dfn[maxn], rnk[maxn], cnt[maxn], ans[maxn], tmp = 0, mx = 0, mxdp = 0;
vector<int>G[maxn];

void add(int v)
{
    cnt[dep[v]]++;
    if (cnt[dep[v]] > mx)
        tmp = dep[v], mx = cnt[dep[v]];
    else if (cnt[dep[v]] == mx)
        tmp = min(tmp, dep[v]);
}

void add_edge(int from, int to)
{
    G[from].push_back(to);
    G[to].push_back(from);
}

void dfs1(int v, int p, int d)
{
    siz[v] = 1, dfn[v] = ++tot, rnk[tot] = v, dep[v] = d, mxdp = max(mxdp, d);
    for (auto& to : G[v])
    {
        if (to == p)
            continue;
        dfs1(to, v, d + 1), siz[v] += siz[to];
        if (!hson[v] || siz[to] > siz[hson[v]])
            hson[v] = to;
    }
}

void dfs2(int v, int p)
{
    for (auto& to : G[v])
    {
        if (to == p || to == hson[v])
            continue;
        dfs2(to, v), tmp = mx = 0;
        for (int i = dfn[to]; i < dfn[to] + siz[to]; i++)
            cnt[dep[rnk[i]]]--;
    }
    if (hson[v])
        dfs2(hson[v], v);

    for (auto& to : G[v])
    {
        if (to == p || to == hson[v])
            continue;
        for (int i = dfn[to]; i < dfn[to] + siz[to]; i++)
            add(rnk[i]);
    }
    add(v), ans[v] = tmp - dep[v];
}

void solve()
{
    dfs1(1, 0, 1), dfs2(1, 0);
    for (int i = 1; i <= N; i++)
        cout << ans[i] << endl;
}

int main()
{
    IOS;
    cin >> N;
    int u, v;
    for (int i = 1; i < N; i++)
        cin >> u >> v, add_edge(u, v);
    solve();

    return 0;
}

208E. Dominant Indices

\(\texttt{Difficulty:2100}\)

题意

一个 \(n(1\le n\le10^5)\) 个节点的森林, \(m(1\le m\le10^5)\) 次询问,每次询问 \(v,p\) ,求 \(v\) 有多少 \(p\) 级表兄弟,即有多少节点和 \(v\)\(p\) 级祖先相同。

题解

考虑将询问离线,询问变为在 \(v\)\(p\) 级祖先处询问有多少深度和 \(v\) 相同的点,减一即为答案。 \(p\) 级祖先可以倍增查找,维护子树内各深度节点数量直接 \(\texttt{dsu on tree}\) 维护即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;

int N, M, root, tot = 0, dfn[maxn], siz[maxn], rnk[maxn], hson[maxn], dep[maxn], ans[maxn], cnt[maxn], fa[20][maxn];
vector<int>G[maxn];
vector<PII>Q[maxn];

void add(int v, int x)
{
	cnt[dep[v]] += x;
}

void add_edge(int from, int to)
{
	G[from].push_back(to);
	G[to].push_back(from);
}

void dfs1(int v, int p, int d)
{
	siz[v] = 1, dfn[v] = ++tot, rnk[tot] = v, dep[v] = d, fa[0][v] = p;
	for (auto& to : G[v])
	{
		if (to == p)
			continue;
		dfs1(to, v, d + 1), siz[v] += siz[to];
		if (!hson[v] || siz[to] > siz[hson[v]])
			hson[v] = to;
	}
}

void dfs2(int v, int p)
{
	for (auto& to : G[v])
	{
		if (to == p || to == hson[v])
			continue;
		dfs2(to, v);
		for (int i = dfn[to]; i < dfn[to] + siz[to]; i++)
			add(rnk[i], -1);
	}
	if (hson[v])
		dfs2(hson[v], v);
	for (auto& to : G[v])
	{
		if (to == p || to == hson[v])
			continue;
		for (int i = dfn[to]; i < dfn[to] + siz[to]; i++)
			add(rnk[i], 1);
	}
	add(v, 1);
	for (auto& [x, y] : Q[v])
		ans[y] = cnt[x] - 1;
}

void solve()
{
	dfs1(root, 0, 1);
	for (int i = 0; i + 1 < 20; i++)
	{
		for (int v = 1; v <= N; v++)
		{
			if (fa[i][v] <= 0)
				fa[i + 1][v] = 0;
			else
				fa[i + 1][v] = fa[i][fa[i][v]];
		}
	}
	cin >> M;
	int v, p;
	for (int i = 1; i <= M; i++)
	{
		cin >> v >> p;
		int k = dep[v];
		for (int j = 19; j >= 0; j--)
		{
			if (!fa[j][v])
				continue;
			if (dep[fa[j][v]] >= k - p)
				v = fa[j][v];
		}
		if (v != root)
			Q[v].push_back(PII(k, i));
	}
	dfs2(root, 0);
	for (int i = 1; i <= M; i++)
		cout << ans[i] << ' ';
	cout << endl;
}

int main()
{
	IOS;
	cin >> N, root = N + 1;
	int p;
	for (int i = 1; i <= N; i++)
	{
		cin >> p;
		if (!p)
			add_edge(root, i);
		else
			add_edge(p, i);
	}
	solve();

	return 0;
}

1721E. Prefix Function Queries

\(\texttt{Difficulty:2200}\)

题意

给一个串 \(s(1\le|s|\le10^6)\)\(q(1\le q\le10^5)\) 个串 \(t(1\le|t_i|\le10)\) 。分别求将串 \(|t_i|\) 接到 \(s\) 后面后最后 \(|t_i|\) 个前缀的 \(\texttt{border}\) 长度。

题解

考虑暴力的做法是先对 \(s\)\(\texttt{kmp}\) ,在依次处理每个 \(t_i\) ,跑剩下部分的 \(\texttt{kmp}\) ,这样的话复杂度为 \(O(q(|s|+|t|))\) 显然不行。 于是有个叫 \(\texttt{kmp}\) 自动机的东西,我们用 \(fail\) 来代表普通 \(\texttt{kmp}\)\(nxt\)\(nxt\) 改为代表该自动机上的 \(trans\) 边,下标从 \(1\) 开始。
\(nxt_{i,j}\) 为从 \(i\) 开始沿着 \(fail\) 树跳到的最大的一个位置 \(k\) ,有 \(s_{k+1}=j\) 。那么我们可以由此对 \(\texttt{kmp}\) 中求前缀函数的过程进行加速, \(\texttt{kmp}\) 中这一过程即从 \(fail_{i-1}\) 不断沿 \(fail\) 树跳到的最大的一个位置 \(k\) ,有 \(s_{k+1}=s_i\) ,如果没有找到则变为 \(0\) 。之后 \(fail_i=k\) ,根据 \(nxt\) 的定义,可以得到 \(fail_i=nxt_{fail_{i-1},s_i}\) 。那么对于 \(nxt_{i,j}\) 的求解,如果 \(j=s_{i+1}\) ,那么 \(nxt_{i,j}=i\) ,否则 \(nxt_{i,j}=nxt_{fail_{i,j}}\)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;
const int maxm = 1000020;

int Q, N;
string S, T[maxn];
int fail[maxm], nxt[maxm][26];

void kmp()
{
	for (int i = 2, j = 0; i <= N; i++)
	{
		while (j >= 1 && S[i - 1] != S[j + 1 - 1])
			j = fail[j];
		if (S[i - 1] == S[j + 1 - 1])
			j++;
		fail[i] = j;
	}
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < 26; j++)
			nxt[i][j] = nxt[fail[i]][j];
		nxt[i][S[i + 1 - 1] - 'a'] = i;
	}
}

void solve()
{
	N = S.length();
	kmp();
	for (int i = 0; i < 10; i++)
		S += '#';
	int A = N + 1;
	for (int i = 1; i <= Q; i++)
	{
		int M = N + T[i].length();
		for (int j = 0; j < T[i].length(); j++)
			S[N + j] = T[i][j];
		for (int j = A, k = fail[N]; j <= M; j++)
		{
			int x = j - 1;
			k = nxt[k][S[j - 1] - 'a'];
			if (S[j - 1] == S[k + 1 - 1])
				k++;
			fail[j] = k, cout << k << ' ';
			for (int l = 0; l < 26; l++)
				nxt[x][l] = nxt[fail[x]][l];
			nxt[x][S[j - 1] - 'a'] = x;
		}
		cout << endl;
	}
}

int main()
{
	IOS;
	cin >> S >> Q;
	for (int i = 1; i <= Q; i++)
		cin >> T[i];
	solve();

	return 0;
}

1139E. Maximize Mex

\(\texttt{Difficulty:2400}\)

题意

\(n(1\le n\le5000)\) 个人, \(m(1\le m\le5000)\) 个社团,每个人有能力值 \(p(0\le p\le n)\) ,以及所属社团 \(c(1\le c\le m)\) 。总共 \(d(1\le d\le n)\) 天,每天开始前第 \(k_i(1\le k_i\le n)\) 会离开。求从每个社团选出 \(1\) 个人(没有就不选)时,团队的最大能力值,其定义为,所有选出来的人的能力值集合的 \(mex\)

题解

考虑将社团设为一部分点,能力值设为另外一部分点,询问离线后改为加边,从小到大进行二分图匹配,如果当前点不能匹配,则记录答案,下个询问从当前节点继续匹配即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 5010;

int N, M, P[maxn], C[maxn], D, K[maxn], ans[maxn], match[maxn * 2];
bool flag[maxn], used[maxn * 2];
vector<int>G[maxn * 2];

void add_edge(int from, int to)
{
	G[from].push_back(to);
	G[to].push_back(from);
}

bool dfs(int v)
{
	used[v] = true;
	for (auto& to : G[v])
	{
		int w = match[to];
		if (w < 0 || !used[w] && dfs(w))
		{
			match[v] = to, match[to] = v;
			return true;
		}
	}

	return false;
}

void solve()
{
	for (int i = 1; i <= N; i++)
	{
		if (flag[i])
			continue;
		add_edge(P[i], C[i] + N);
	}
	int tmp = 0;
	mst(match, -1);
	for (int i = D, j = 0; i >= 1; i--)
	{
		for (; j <= N + M; j++)
		{
			if (match[j] < 0)
			{
				mst(used, 0);
				if (dfs(j))
					tmp++;
				else
					break;
			}
		}
		add_edge(P[K[i]], C[K[i]] + N), ans[i] = tmp;
	}
	for (int i = 1; i <= D; i++)
		cout << ans[i] << endl;
}

int main()
{
	IOS;
	cin >> N >> M;
	for (int i = 1; i <= N; i++)
		cin >> P[i];
	for (int i = 1; i <= N; i++)
		cin >> C[i];
	cin >> D;
	for (int i = 1; i <= D; i++)
		cin >> K[i], flag[K[i]] = true;
	solve();

	return 0;
}

1721D. Maximum AND

\(\texttt{Difficulty:1800}\)
来点水题

题意

长为 \(n(1\le n\le10^5)\) 的数列 \(a,b(0\le a_i,b_i<2^{30})\) ,记 \(f(a,b)\) 为 & \(_{i=1}^n a_i\oplus b_i\) ,现在你可以任意排列 \(b\) ,求 \(f(a,b)\) 的最大值。

题解

我们按位考虑,对于第 \(k\) 位,如果其能够为 \(1\) ,我们记 \(x_i,y_i\) 分别为 \(a_i,b_i\)\(2^k\) 后的数,那么就必须存在一种排列,使得所有 \(x_i\oplus y_i\) & \(tmp\) 仍然为 \(tmp\) 。由于 \(tmp\) 中为 \(0\) 的位是无所谓的,我们可以先将所有的 \(x_i,y_i\) 与上 \(tmp\) 。接下来考虑如何判断,发现我们对 \(x,y\) 按不同的顺序排序,然后对次排列进行 \(check\) 即可判断了,因为当前所有的数或上 \(tmp\) 后仍为 \(tmp\) ,如果最大的数字想要异或一个数达到 \(tmp\) ,那么只可能去异或当前最小的(因为不去异或最小的那么最小的就不可能找到一个数异或为 \(tmp\)),于是可以在 \(O(nlognloga)\) 解决。

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;

int T, N, A[maxn], B[maxn], X[maxn], Y[maxn];

void solve()
{
	int ans = 0;
	for (int i = 29; i >= 0; i--)
	{
		bool flag = true;
		int tmp = (ans | (1 << i)) >> i;
		for (int j = 1; j <= N; j++)
			X[j] = (A[j] >> i) & tmp, Y[j] = (B[j] >> i) & tmp;
		sort(X + 1, X + N + 1), sort(Y + 1, Y + N + 1);
		for (int j = 1; j <= N; j++)
		{
			int num = X[j] ^ Y[N - j + 1];
			if (num != tmp)
			{
				flag = false;
				break;
			}
		}
		if (flag)
			ans |= (1 << i);
	}
	cout << ans << endl;
}

int main()
{
	IOS;
	cin >> T;
	while (T--)
	{
		cin >> N;
		for (int i = 1; i <= N; i++)
			cin >> A[i];
		for (int i = 1; i <= N; i++)
			cin >> B[i];
		solve();
	}

	return 0;
}
posted @ 2022-08-26 11:55  Prgl  阅读(39)  评论(0)    收藏  举报