ATcoder ABC 357 补题记录(A~F)
A
按照顺序直接模拟即可。
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;i++)
#define G(i,x,y) for(int i=x;i>=y;i--)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#ifdef int
#define inf (7611257611378358090ll/2)
#else
#define inf 0x3f3f3f3f
#endif
using namespace std;
const int N = 1000100;
int a[N], n;
signed main() {
int n, m, cnt = 0;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int h;
cin >> h;
m -= h;
if (m >= 0) {
cnt++;
} else {
break;
}
}
cout << cnt << '\n';
}
B
直接比较大写字母和小写字母的数量即可。
时间复杂度为 \(O(n)\)。
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;i++)
#define G(i,x,y) for(int i=x;i>=y;i--)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#ifdef int
#define inf (7611257611378358090ll/2)
#else
#define inf 0x3f3f3f3f
#endif
using namespace std;
const int N = 1000100;
int a[N];
signed main() {
string s;
cin >> s;
int c1 = 0, c2 = 0;
for (auto &x : s) {
if (islower(x)) c1++;
else c2++;
}
if (c1 < c2) {
for (auto &x : s) x = toupper(x);
cout << s << '\n';
} else {
for (auto &x : s) x = tolower(x);
cout << s << '\n';
}
}
C
模拟题。
考虑对于一个 \(3^n\times 3^n\) 的矩阵,可以将其分割为 \(9\) 个 \(3^{n-1}\times 3^{n-1}\) 的矩阵,其中这 \(9\) 个矩阵又构成了 \(3\times 3\) 的大矩阵。
因此直接计算出 \(9\) 个矩阵的左上角和右下角的坐标,然后除了中间的一个矩阵以外其他的矩阵继续递归处理即可。
时间复杂度为 \(O(3^{2n})\)。
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;i++)
#define G(i,x,y) for(int i=x;i>=y;i--)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#ifdef int
#define inf (7611257611378358090ll/2)
#else
#define inf 0x3f3f3f3f
#endif
using namespace std;
const int N = 1000100;
int a[N], n;
char s[5999][5999];
void dfs(int n, int x = 1, int y = 1) {
if (n == 0) {
s[x][y] = '#';
} else {
int key = 1;
for (int i = 1; i < n; i++) {
key = key * 3;
}
dfs(n - 1, x, y);
dfs(n - 1, x + key, y);
dfs(n - 1, x + key + key, y);
dfs(n - 1, x, y + key);
dfs(n - 1, x + key + key, y + key);
dfs(n - 1, x, y + key + key);
dfs(n - 1, x + key, y + key + key);
dfs(n - 1, x + key + key, y + key + key);
}
}
signed main() {
cin >> n;
if (!n) {
cout << "#\n";
} else {
int key = 1;
for (int i = 0; i < n; i++) {
key *= 3;
}
for (int i = 1; i <= key; i++) {
for (int j = 1; j <= key; j++) {
s[i][j] = '.';
}
}
dfs(n);
for (int i = 1; i <= key; i++, cout << '\n') {
for (int j = 1; j <= key; j++) {
cout << s[i][j];
}
}
}
}
D
考虑经典套路。这里重定义 \(|n|\) 表示 \(n\) 的位数,即 to_string(n).size()
(请使用 C++11 及以上版本)。\(S_n\) 表示 \(N=n\) 时的答案。
首先考虑 \(|n|\le 1\) 的情况。此时沿用 P4884 的解决方案。有 \(S_n=n\times \frac{10^n-1}{9}\)。
问题是 \(|n|>1\) 时的情况。容易发现此时等式 \(\large S_n=n\times \frac{10^{n\times|n|}}{10^k-1}\) 成立。证明显然。
然后考虑计算这个东西。当 \(n=10^{18}\) 时 \(n\times |n|=10^{18}\times 19=1.9\times 10^{18}>2^{64}-1\) 是很恶心的一点,所以请使用 __int128
来计算答案。其实也可以使用 atcoder::modint998244353
来计算答案。
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;i++)
#define G(i,x,y) for(int i=x;i>=y;i--)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#ifdef int
#define inf (7611257611378358090ll/2)
#else
#define inf 0x3f3f3f3f
#endif
using namespace std;
const int mod = 998244353;
int ksm(int a, __int128 b, int c) {
if (!b) return 1;
int ans = ksm(a, b / 2, c);
ans = ans * ans % c;
if (b & 1) ans = ans * a % c;
return ans;
}
signed main() {
int n;
cin >> n;
int len = to_string(n).size();
int key = ksm(10, (__int128)(len) * n, mod);
key = (key + mod - 1) % mod;
int ii = ksm((ksm(10, len, mod) + mod - 1) % mod, mod - 2, mod);
// cout << "dbg " << ii << ' ' << key << '\n';
cout << (n % mod) * ii % mod * key % mod << '\n';
}
E
不理解为什么题目保证边数不大于点数。这个题 \(m>n\) 不是也可以做吗。
首先建反图,然后考虑使用 Tarjan 算法将有向图中的每一个强连通分量在新图中缩为一个点,然后剩下一个 DAG 森林考虑搞一下。
设 \(f_i\) 表示可以到达新图 \(i\) 点的点的数量,\(g_i\) 表示以新图 \(i\) 点结尾的不同的路径的总数目。
那么对于每一条 \(j\to i\) 的边,必然有:
- \(f_i=f_i+f_j\)。
- \(g_i=g_i+f_j\times \text{CNT}_i\)。
其中 \(\text{CNT}_i\) 表示新图中 \(i\) 点在原图中对应的强连通分量中点的数目。
因为新的图是 DAG 森林,所以每一次从入度为 \(0\) 的结点开始转移,在拓扑排序的过程中就可以直接求出答案。
因为不同的 \(i\) 对应的 \(g_i\) 对应的路径是互不重复的,所以答案就是 \(\sum\limits_{i=1}^ng_i\),不需要容斥。
时间复杂度为 \(O(n+m)\)。如果写丑了可能会出问题。
#include<bits/stdc++.h>
#define int long long
#define said(...)
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;++i)
#define G(i,x,y) for(int i=x;i>=y;--i)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#define inf (7611257611378358090ll/2)
using namespace std;
const int N = 500100;
const int mod = 998244353;
stack<int> stk;
vector<int> z[N], scc[N];
int idx, tot, instk[N], dfn[N], low[N], bel[N], cnt[N], dis[N], vis[N], en[N], deg[N];
struct Edg { int u, v; } ed[N];
void dfs(int u) {
dfn[u] = low[u] = ++idx;
stk.push(u), instk[u] = true;
W(z, j, u) {
if (!dfn[j]) {
dfs(j);
low[u] = min(low[u], low[j]);
} else if (instk[j]) {
low[u] = min(low[u], dfn[j]);
}
}
if (dfn[u] == low[u]) {
tot++;
while (stk.top() != u) {
int t = stk.top();
stk.pop(), instk[t] = false;
bel[t] = tot, scc[tot].pb(t);
}
int t = stk.top();
stk.pop(), instk[t] = false;
bel[t] = tot, scc[tot].pb(t);
}
}
int a[N], dp[N], gg[N];
auto main() [[O3]] -> signed {
int n;
cin >> n;
F(i, 1, n) {
int x;
cin >> x;
a[i] = x;
z[i].push_back(x);
}
F(i, 1, n) {
if (!dfn[i]) {
dfs(i);
}
}
F(i, 1, n) {
z[i].clear();
}
F(i, 1, n) {
if (bel[i] != bel[a[i]]) {
deg[bel[i]]++;
z[bel[a[i]]].push_back(bel[i]);
}
}
queue<int> q;
F(i, 1, tot) {
if (!deg[i]) {
q.push(i);
}
gg[i] = scc[i].size();
dp[i] = scc[i].size() * scc[i].size();
}
while (q.size()) {
int f = q.front();
q.pop();
for (auto &g : z[f]) {
gg[g] = (gg[g] + gg[f]) % mod;
dp[g] = (dp[g] + scc[g].size() * gg[f] % mod) % mod;
if (!--deg[g]) {
q.push(g);
}
}
}
cout << accumulate(dp + 1, dp + tot + 1, 0ll) << '\n';
}
F
区间修改区间查询很显然的线段树。
考虑对于线段树的每一个结点维护 \(A\) 的和,\(B\) 的和,\(A\) 的懒标记,\(B\) 的懒标记,当前区间内的答案的和。
首先 push_up
的时候直接将 \(A\),\(B\) 的和和答案的和全部直接把左右儿子的值加起来即可。
修改的时候:
- 若当前修改的是 \(A\) 的值,则答案从 \(a_l\times b_l+a_{l+1}\times b_{l+1}+\ldots+a_r\times b_r\) 变为了 \((a_l+v)\times b_l+(a_{l+1}+v)\times b_{l+1}+\ldots+(a+r+v)\times b_r\)。显然后面的式子为 \((a_l\times b_l+a_{l+1}\times b_{l+1}+\ldots+a_r\times b_r)+v\times (b_l+b_{l+1}+b_{l+2}+\ldots+b_r)=(a_l\times b_l+a_{l+1}\times b_{l+1}+\ldots+a_r\times b_r)+v\times B\)。所以更新答案的时候直接把 \(A\) 加上 \((r-l+1)\times v\),总和加上 \(v\times B\) 即可。
- 若当前修改的为 \(B\) 的值,则和 \(A\) 的情况一样,\(B\) 加上 \((r-l+1)\times v\),总和加上 \(v\times A\) 即可。
下传标记直接按照 \(A\),\(B\) 的顺序下传即可,时间复杂度为 \(O(n\log n)\)。注意不要爆 long long
。
#include<bits/stdc++.h>
#define int long long
#define said(...)
#define pb push_back
#define em emplace_back
#define F(i,x,y) for(int i=x;i<=y;++i)
#define G(i,x,y) for(int i=x;i>=y;--i)
#define W(G,i,x) for(auto&i:G[x])
#define W_(G,i,j,x) for(auto&[i,j]:G[x])
#define add(x,y) z[x].em(y)
#define add_(x,y) add(x,y),add(y,x)
#define Add(x,y,d) z[x].em(y,d)
#define Add_(x,y,z) Add(x,y,z),Add(y,x,z);
#define inf (7611257611378358090ll/2)
using namespace std;
const int N = 200100;
const int mod = 998244353;
int a[N], b[N];
struct Node {
int l, r, a, b, taga, tagb, sum;
void init(int p) {
l = r = p;
a = ::a[p];
a %= mod;
b = ::b[p];
b %= mod;
taga = tagb = 0;
sum = a * b % mod;
}
void c1(int v) {
v %= mod;
taga += v, a += (r - l + 1) * v;
sum += v * b; sum %= mod;
taga %= mod, a %= mod;
// sum += b * v;
}
void c2(int v) {
v %= mod;
tagb += v, b += (r - l + 1) * v;
sum += v * a % mod; sum %= mod;
tagb %= mod, b %= mod;
// sum = a * b;
}
} z[N << 2];
Node operator+(const Node &l, const Node &r) {
Node res;
res.l = l.l, res.r = r.r;
res.a = l.a + r.a, res.b = l.b + r.b, res.sum = l.sum + r.sum;
res.a %= mod, res.b %= mod, res.sum %= mod;
res.taga = res.tagb = 0;
return res;
}
void build(int l, int r, int rt) {
if (l == r) {
return z[rt].init(l);
}
int mid = l + r >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void pud(int rt) {
if (z[rt].taga != 0) {
z[rt << 1].c1(z[rt].taga);
z[rt << 1 | 1].c1(z[rt].taga);
z[rt].taga = 0;
}
if (z[rt].tagb != 0) {
z[rt << 1].c2(z[rt].tagb);
z[rt << 1 | 1].c2(z[rt].tagb);
z[rt].tagb = 0;
}
}
void modify1(int l, int r, int rt, int ll, int rr, int v) {
if (ll <= l && r <= rr) {
return z[rt].c1(v);
}
int mid = l + r >> 1;
pud(rt);
if (ll <= mid) {
modify1(l, mid, rt << 1, ll, rr, v);
}
if (mid < rr) {
modify1(mid + 1, r, rt << 1 | 1, ll, rr, v);
}
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void modify2(int l, int r, int rt, int ll, int rr, int v) {
if (ll <= l && r <= rr) {
return z[rt].c2(v);
}
int mid = l + r >> 1;
pud(rt);
if (ll <= mid) {
modify2(l, mid, rt << 1, ll, rr, v);
}
if (mid < rr) {
modify2(mid + 1, r, rt << 1 | 1, ll, rr, v);
}
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
int query(int l, int r, int rt, int ll, int rr, int v) {
if (ll <= l && r <= rr) {
return z[rt].sum;
}
int mid = l + r >> 1;
pud(rt);
int s = 0;
if (ll <= mid) {
s = (s + query(l, mid, rt << 1, ll, rr, v)) % mod;
}
if (mid < rr) {
s = (s + query(mid + 1, r, rt << 1 | 1, ll, rr, v)) % mod;
}
return s % mod;
}
auto main() [[O3]] -> signed {
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
build(1, n, 1);
while (q--) {
int o, l, r, x;
cin >> o >> l >> r;
if (o < 3) {
cin >> x;
}
if (o == 1) {
modify1(1, n, 1, l, r, x);
} else if (o == 2) {
modify2(1, n, 1, l, r, x);
} else {
cout << query(1, n, 1, l, r, x) << '\n';
}
}
}
G
咕咕咕。
本文来自博客园,作者:yhbqwq,转载请注明原文链接:https://www.cnblogs.com/yhbqwq/p/18239046,谢谢QwQ