Educational Codeforces Round 178 (Rated for Div. 2)

Dashboard - Educational Codeforces Round 178 (Rated for Div. 2) - Codeforces

Problem - A - Codeforces
数学

充要条件为 \(a+b+c\equiv 0\bmod 3\),且 \(c-b\ge b-a\)。即先将 \(a\) 加到 \(b\),之后均分。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}



void solve()
{
    int a = read(), b = read(), c = read();
	int sum = a + b + c;
	if(sum % 3 != 0)
	{
		printf("NO\n");
		return ;
	}
	if(c - b >= b - a) printf("YES\n");
	else printf("NO\n");
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - B - Codeforces
贪心

由于只能操作一次,对于 \(k\),答案只可能是后 \(k\) 个数或者后 \(k-1\) 个数+前 \(n-k\) 个数中的最大值。

分别记录后缀和与前缀 \(\max\) 即可。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const ll N = 2e5 + 5;
ll n, a[N], sum[N], mx[N];

void solve()
{
	n = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	mx[1] = a[1];
	for(int i = 2; i <= n; ++i) mx[i] = max(mx[i - 1], a[i]);
	sum[n] = a[n];
	for(int i = n - 1; i >= 1; --i) sum[i] = sum[i + 1] + a[i];
	for(int i = n; i >= 1; --i)
	{
		if(mx[i - 1] > a[i]) printf("%lld ", sum[i] - a[i] + mx[i - 1]);
		else printf("%lld ", sum[i]);
	}
	printf("\n");
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - C - Codeforces
博弈论 分类讨论

分别考虑 \(1\)\(n\) 在不在 \(Alice\) 手上。

\(case1\)\(1\)\(n\) 都在 \(Alice\) 手上,\(Alice\) 胜。

\(case2\)\(1\)\(Alice\) 手上,\(n\)\(Bob\) 手上,若 \(Bob\) 只有 \(n\)\(Alice\) 胜,否则 \(Bob\) 胜。

\(case3\)\(1\)\(Bob\) 手上,\(n\)\(Alice\) 手上,谁拿 \(n-1\) 谁胜。

\(case4\)\(1\)\(n\) 都在 \(Bob\) 手上,\(Bob\) 胜。

注意特殊处理 \(n=2\) 的情况。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 55;
int n;
char s[N];
int a, b, cnt1, cnt2;

void Solve()
{
	if(s[1] == 'A') printf("Alice\n");
	else printf("Bob\n");
}

void solve()
{
	n = read(), cnt1 = cnt2 = 0;
	scanf(" %s", s + 1);
	for(int i = 1; i <= n; ++i)
	{
		if(s[i] == 'A') ++cnt1;
		else ++cnt2;
	}
	if(n == 2)
	{
		Solve();
		return ;
	}
	if(s[1] == 'A') a = 1;
	else a = 0;
	if(s[n] == 'A') b = 1;
	else b = 0;
	if(a == 1 && b == 1)
	{
		printf("Alice\n");
	}else if(a == 1 && b == 0)
	{
		if(cnt2 == 1) printf("Alice\n");
		else printf("Bob\n");
	}else if(a == 0 && b == 1)
	{
		if(s[n - 1] == 'A') printf("Alice\n");
		else printf("Bob\n");
	}else printf("Bob\n");
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - D - Codeforces
数论 二分

可以先将硬币收集起来,再逐一分配,可以不必都分配。

可以猜到,两两互质的总和最小的 \(k\) 个数是前 \(k\) 个质数。

筛出足够多的质数后求前缀和数组,询问在前缀和数组上二分。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 1e7 + 5;
int prime[N], cnt;
bool vis[N];
ll s[N];

void init()
{
	for(int i = 2; i <= 10000000; ++i)
	{
		if(!vis[i]) prime[++cnt] = i;
		for(int j = 1; j <= cnt && prime[j] * i <= 10000000; ++j)
		{
			vis[prime[j] * i] = 1;
			if(i % prime[j] == 0) break;
		}
	}
	for(int i = 1; i <= 400000; ++i) s[i] = s[i - 1] + prime[i];
}

ll n, a[N], S[N];

void solve()
{
	n = read();
	ll sum = 0;
	for(int i = 1; i <= n; ++i) a[i] = read(), sum += a[i];
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; ++i) S[i] = S[i - 1] + a[i];
	int l = 0, r = n - 1;
	while(l < r)
	{
		int mid = (l + r) >> 1;
		if(sum - S[mid] >= s[n - mid]) r = mid;
		else l = mid + 1;
	}
	printf("%d\n", l);
}

int main()
{
	init();
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - E - Codeforces
贪心 字符串 DP

\(last_{i,j}\) 表示字符串 \(s\) 的以 \(i\) 开头的后缀中,第一个字符 \(j\) 的位置。对于每一个字符串 \(t\),一定是从前往后贪心的匹配字符串 \(s\),记 \(p\) 为字符串 \(s\) 与字符串 \(t\) 的末尾匹配的位置。

问题变为,找到一个长度最小的字符串,使得它与字符串 \(s\) 的以 \(p\) 开头的后缀无法匹配。

\(dp_{i}\) 表示与字符串 \(s\) 的以 \(i\) 开头的后缀无法匹配字符串的最小长度,转移时枚举下一个字符填什么。

\[dp_{i}=\min_{j}\{dp_{last_{i,j}+1}+1\} \]

代码中由于读错题,处理的在 \(t\) 串左边加字符串的情况,将字符串翻转即为答案。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int inf = 0x3f3f3f3f;
const int N = 1e6 + 5;
int n, K, q;
char s[N];
char t[N];
int last[N][27], dp[N];

void solve()
{
	n = read(), K = read();
	scanf(" %s", s + 1);
	for(int i = 1; i <= (n + 1) / 2; ++i) swap(s[i], s[n - i + 1]);
	dp[0] = 1;
	for(int i = 0; i <= n + 1; ++i)
		for(int j = 0; j < 26; ++j)
			last[i][j] = 0;
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 0; j < K; ++j) last[i][j] = last[i - 1][j];
		last[i][s[i] - 'a'] = i;
		dp[i] = inf;
		for(int j = 0; j < K; ++j)
		{
			if(last[i][j] == 0) dp[i] = 1;
			else dp[i] = min(dp[i], dp[last[i][j] - 1] + 1);
		}
	}
	q = read();
	while(q--)
	{
		scanf(" %s", t + 1);
		int len = strlen(t + 1);
		for(int i = 1; i <= (len + 1) / 2; ++i) swap(t[i], t[len - i + 1]);
		int now = n + 1;
		for(int i = len; i >= 1; --i)
		{
			if(now == 0) break;
			now = last[now - 1][t[i] - 'a'];
		}
		if(now == 0) printf("0\n");
		else printf("%d\n", dp[now - 1]);
		for(int i = 0; i <= len + 1; ++i) t[i] = 0;
	}
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - F - Codeforces
暴力枚举

注意到一个数 \(x\) 可以分成 \(3\) 部分,设为 \(ABC\),其中 \(B\) 是一个 \(0\sim 8\) 的数,\(C\) 是由若干个 \(9\) 构成的数,\(A\) 任意。

剪枝:由于 \(x+1\) 不会改变 \(A\),因此我们只枚举数位单调不降的 \(A\),由于 \(A\) 不能有前导零,所以在第一个数字后面插入若干个 \(0\)

经过剪枝搜索,数位不多于 \(8\) 位的 \(A\) 不超过 \(200000\) 个,再枚举 \(B\),枚举 \(C\) 的长度,计算答案。

然而其中会有重复答案,可以将得到的所有数位从大到小构成一个数后排序去重,只需要记录最小的能构成这个数的 \(x\)

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

vector<int> a[11];

void dfs(int len, ll now, bool flag, int last)
{
	if(len == 9) return ;
	a[len].emplace_back(now);
	if(!flag) dfs(len + 1, now * 10, flag, last);
	for(int i = last; i <= 9; ++i) dfs(len + 1, now * 10 + i, 1, i);
}

vector< pair<ll, int> > mp;

ll pow10[11], b[11];
vector<int> ans;

void init()
{
	pow10[0] = 1;
	for(int i = 1; i <= 10; ++i) pow10[i] = pow10[i - 1] * 10;
	b[1] = 9;
	for(int i = 2; i <= 10; ++i) b[i] = b[i - 1] * 10 + 9;

	a[0].emplace_back(0);
	for(int i = 1; i <= 9; ++i) dfs(1, i, 0, i);
	
	int sum = 0;
	for(int i = 0; i <= 10; ++i) sum += a[i].size();
	for(int i = 0; i <= 8; ++i)
		for(auto X : a[i])
			for(int mid = 0; mid <= 8; ++mid)
				for(int len = 0; len + i <= 8; ++len)
				{
					vector<int> t;
					ll x = (X * 10 + mid) * pow10[len] + b[len], t1 = x, t2 = x + 1;
					if(x == 0) continue;
					do
					{
						t.emplace_back(t1 % 10);
						t1 /= 10;
					}while(t1);
					do
					{
						t.emplace_back(t2 % 10);
						t2 /= 10;
					} while (t2);
					sort(t.begin(), t.end());
					ll temp = 0;
					for(int i = t.size() - 1; i >= 0; --i) temp = temp * 10 + t[i];
					mp.emplace_back(pair<ll, int>(temp, x));
				}
	sort(mp.begin(), mp.end());
	for(int i = 0; i < mp.size(); ++i)
	{
		if(i == 0 || mp[i].first != mp[i - 1].first ) ans.emplace_back(mp[i].second );
	}
	sort(ans.begin(), ans.end());
}

void solve()
{
	int n = read();
	int A = upper_bound(ans.begin(), ans.end(), n) - ans.begin();
	printf("%d\n", A);
}

int main()
{
	init();
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - G - Codeforces
\(3\) 性质 基环树性质 线段树分治 边带权并查集

思路借鉴了这篇博客:IkunTeddy的文章

对于一个有向图,经过染色后,一个强联通分量里面的点的颜色一定是相同的,于是可以先缩点成 \(DAG\),又因为可以按照拓扑排序染色,所以 \(DAG\) 上每个点的颜色都是独立的,设有 \(cnt\) 个,答案为 \(k^{cnt}\)

首先加边删边不好操作,使用线段树分治变为加边操作。

由于是基环树,图上只有一个环,且每个点至多属于一个环,转化为统计环上的点的个数。

考虑将有向边看作无向边,用并查集维护连通性,在依次加边的过程中若两个点是联通的,需要知道这两个点在树上的距离。

由于模 \(3\) 的性质,在 \(k^{cnt}\) 的指数上取模即为 \(cnt\bmod 2\),只和联通块的个数的奇偶性有关,可以用带权并查集维护到根的联通块个数的奇偶性。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int n, q;
int g[N], t[N];
int qk[N];
int f[N], s[N], Size[N];
ll ans[N];
pair<int, int> find(int x)
{
	if(x == f[x]) return pair<int, int>(x, 0);
	pair<int, int> temp = find(f[x]);
	return pair<int, int>(temp.first , temp.second ^ s[x]);
}

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

struct Edge
{
	int u, v;
	Edge(){ u = v = 0; }
	Edge(int u, int v): u(u), v(v){}
};

vector<Edge> E[N << 2];

int now;

pair<int, int> sta[N];
int top;

void dfs(int k, int l, int r)
{
	int flag1 = top, flag2 = now;
	for(Edge it : E[k])
	{
		int u = it.u , v = it.v ;
		pair<int, int> fu = find(u), fv = find(v);
		if(fu.first == fv.first ) now ^= fu.second ^ fv.second ;
		else
		{
			int U = fu.first , V = fv.first ;
			if(Size[U] < Size[V]) swap(U, V);
			sta[++top] = pair<int, int>(U, V);
			f[V] = f[U], Size[U] += Size[V], s[V] = fu.second ^ fv.second ^ 1;
		}
	}
	if(l != r)
	{
		int mid = (l + r) >> 1;
		dfs(ls(k), l, mid);
		dfs(rs(k), mid + 1, r); 
	}else
	{
		if(qk[l] % 3 == 2) ans[l] = (now & 1) ? 2 : 1;
		else ans[l] = qk[l] % 3;
	}
	now = flag2;
	while(top != flag1)
	{
		int u = sta[top].first , v = sta[top].second ;
		f[v] = v, Size[u] -= Size[v], s[v] = 0;
		--top;
	}
}

void update(int k, int l, int r, int L, int R, int u, int v)
{
	if(L <= l && r <= R)
	{
		E[k].emplace_back((Edge){u, v});
		return ;
	}
	int mid = (l + r) >> 1;
	if(L <= mid) update(ls(k), l, mid, L, R, u, v);
	if(R > mid) update(rs(k), mid + 1, r, L, R, u, v);
}

void solve()
{
	n = read(), q = read();
	for(int i = 1; i <= n; ++i) g[i] = read(), t[i] = 1;
	for(int i = 1; i <= q; ++i)
	{
		int x = read(), y = read();
		qk[i] = read();
		if(t[x] <= i - 1) update(1, 1, q, t[x], i - 1, x, g[x]);
		g[x] = y, t[x] = i;
	}
	for(int i = 1; i <= n; ++i) update(1, 1, q, t[i], q, i, g[i]);
	now = n & 1;
	for(int i = 1; i <= n; ++i) f[i] = i, s[i] = 0, Size[i] = 1;
	dfs(1, 1, q);
	for(int i = 1; i <= q; ++i) printf("%lld\n", ans[i]);
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}
posted @ 2025-05-01 16:34  梨愁浅浅  阅读(112)  评论(0)    收藏  举报