Atcoder Beginner Contest 258 (D, E, G)
D - Trophy
有 \(N\) 个关卡,通过每关需要 \(A_i + B_i\) 时间,扫荡需要 \(B_i\) 时间,必须通过后才能扫荡,问一共打完 \(X\) 关最少需要多长时间
\(1 \le N\le 2 \times 10^5\)
\(1\le A[i],B[i] \le 10^9 (1\le i \le N)\)
\(1\le X \le 10^9\)
考虑以每一关为结尾,维护到这一关的最小扫荡时间 \(v\),当前贡献是 \(\sum_{i=1} ^ {N} (A[i] + B[i]) + (X-i) \times v\) 取最小
代码
typedef long long ll;
const int N = 2e5 + 10;
ll a[N], b[N];
void solve() {
int n, x;
cin >> n >> x;
for (int i = 1; i <= n; i++) cin >> a[i] >> b[i];
ll mn = 1e18;
ll ans = 1e18 + 2e9, sum = 0;
for (int i = 1; i <= min(n, x); i++) {
ll cnt = x - i;
sum += a[i] + b[i];
mn = min(mn, b[i]);
ans = min(ans, sum + mn * cnt);
}
cout << ans << '\n';
}
E - Packing Potatoes
给定一个序列 \(W\) 长度为 \(N\) ,\(W_i\) 代表土豆的重量,现在有一条传送带,按照 \(W\) 序列 依次把土豆放在传送带上(\(W_0,W_1,...W_{n-1}, W_0...\)),传送带下面有一个麻袋来接土豆,如果麻袋内土豆的总重量大于等于 \(X\) 的话,暂停运输,换一个空的麻袋继续来装。
给定 \(Q\) 次询问,每次给定一个正整数 \(K_i\) ,问第 \(K_i\) 个麻袋里面装了多少个土豆。
\(1\le N,Q \le 2\times 10^5\) \(1\le X \le 10^9\) \(1\le W_i \le 10^9 (0\le i \le N-1)\)
\(1\le K_i \le 10^{12}\) 保证所有输入都是正整数
考虑到从每个点开始装到装满的的点是固定的,可以通过前缀和+二分预处理出以每个点为起点到的终点和装了多少个土豆。有个小技巧,将数组扩大2倍来模拟环。然后可以发现每个点最多只有一次做为起点,可以去 \(O(n)\) 的去找循环节。注意有可能一开始不是循环的,可能是到某个点之后开始循环,这时候 \(K\) 要减掉不是循环的部分。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 4e5 + 10;
int a[N], sum[N];
PII vis[N];
bool vis2[N];
void solve() {
int n, q, x;
cin >> n >> q >> x;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = n + 1; i <= 2 * n; i++) {
a[i] = a[i - n];
}
for (int i = 1; i <= 2 * n; i++) {
sum[i] = sum[i - 1] + a[i];
}
for (int i = 1; i <= n; i++) {
int tmp = sum[n + i - 1] - sum[i - 1];
int y = x % tmp, s = x / tmp;
if (!y) {
vis[i].first = i - 1;
vis[i].second = s * n;
continue;
}
int l = i, r = n + i - 1;
while (l < r) {
int mid = l + r >> 1;
if (sum[mid] - sum[i - 1] + tmp * s >= x) r = mid;
else l = mid + 1;
}
vis[i].first = (l > n ? l - n : l);
vis[i].second = l - i + 1 + s * n;
}
int st = 1;
vector<int> v, v1, v2;
while (!vis2[st]) {
v.push_back(st);
vis2[st] = 1;
st = vis[st].first + 1;
if (st == n + 1) st = 1;
}
int id;
for (int i = 0; i < v.size(); i++) {
if (v[i] != st) v1.push_back(v[i]);
else {
id = i;
break;
}
}
for (int i = id; i < v.size(); i++) {
v2.push_back(v[i]);
}
int len = v1.size(), len2 = v2.size();
while(q--) {
int k;
cin >> k;
k--;
if (k < len) {
cout << vis[v1[k]].second << "\n";
} else {
k -= len;
int y = k % len2;
cout << vis[v2[y]].second << "\n";
}
}
}
G - Triangle
给定一个 \(N\) 个点的无向图,形式是 \(N \times N\) 的一个矩阵 \(A\) , \(A_{i j}\) 如果为1 说明 \(i\) 到 \(j\) 有一条边,否则没边。求三元环的个数。 \(1\le N \le 3000\)
用 bitset 枚举两个点,这两个点能到的点的集合相与 1的个数就是对答案的贡献,时间复杂度 \(O(n^3 / 64)\)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3010;
bitset<3000> g[N];
void solve() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
string s;
cin >> s;
for (int j = i; j < n; j++) {
if (s[j] == '1') g[i][j] = 1;
}
}
ll ans = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (g[i][j]) {
ans += (g[i] & g[j]).count();
}
}
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}