Educational Codeforces Round 178 (Rated for Div. 2)
Dashboard - Educational Codeforces Round 178 (Rated for Div. 2) - 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;
}
由于只能操作一次,对于 \(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\) 开头的后缀无法匹配字符串的最小长度,转移时枚举下一个字符填什么。
代码中由于读错题,处理的在 \(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;
}
注意到一个数 \(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;
}

浙公网安备 33010602011771号