Atcoder ABC321 笔记
A - 321-like Checker \(\color{gray}{22}\)
直接模拟
void solve() {
int n;
cin >> n;
int lst = -1;
for(int i = n; i; i /= 10) {
int u = i % 10;
if(u <= lst) {
cout << "No" << endl;
return;
}
lst = u;
}
cout << "Yes" << endl;
}
B - Cutoff \(\color{gray}{220}\)
直接枚举分数,时间复杂度 \(O(n^2\log n)\)
void solve() {
int n, x;
cin >> n >> x;
vector<int> a(n), b;
REP(i, n - 1) cin >> a[i];
FOR(i, 0, 100) {
b = a;
b[n - 1] = i;
sort(ALL(b));
int s = 0;
FOR(i, 1, n - 2) s += b[i];
if(s >= x) {
cout << i << endl;
return;
}
}
cout << -1 << endl;
}
C - 321-like Searcher \(\color{brown}{591}\)
符合条件的数比较少,直接模拟。
void solve() {
int k;
cin >> k;
vector<ll> a;
REP(i, 1 << 10) {
ll res = 0;
ROF(j, 9, 0) {
if(i >> j & 1) {
res = res * 10 + j;
}
}
a.push_back(res);
}
sort(ALL(a));
cout << a[k + 1] << endl;
}
D - Set Menu \(\color{green}{806}\)
对于每一个数,会有可以直接加的和封顶的,
二分这个边界即可。
void solve() {
int n, m, p;
cin >> n >> m >> p;
vector<int> a(n), b(m);
REP(i, n) cin >> a[i];
REP(i, m) cin >> b[i];
sort(ALL(a));
vector<ll> s(n + 1);
FOR(i, 0, n - 1) s[i + 1] = a[i] + s[i];
ll ans = 0;
REP(i, m) {
int u = lower_bound(ALL(a), p - b[i]) - BG(a);
ans += s[u];
ans += 1ll * u * b[i];
ans += 1ll * p * (n - u);
}
cout << ans << endl;
}
E - Complete Binary Tree \(\color{blue}{1627}\)
枚举 lca,再二分距离为 \(k\) 的节点个数。
ll n, x, k;
ll calc(ll u, ll k) {
if(u > n || k < 0) return 0;
ll l = u, r = u;
while(k--) {
l <<= 1;
r <<= 1; r |= 1;
if(l > n) return 0;
}
if(r <= n) return r - l + 1;
else return n - l + 1;
}
void solve() {
cin >> n >> x >> k;
ll lst = LINF << 1, ans = 0;
while(x) {
ans += calc(x, k);
ans -= calc(lst, k - 1);
k--;
lst = x;
x >>= 1;
}
cout << ans << endl;
}
F - #(subset sum = K) with Add and Erase \(\color{blue}{1645}\)
第一种就是基本的背包
第二种直接倒过来操作即可
const int N = 5e3 + 5;
int q, k;
int f[N];
void solve() {
cin >> q >> k;
f[0] = 1;
REP(ii, q) {
string opt;
int x;
cin >> opt >> x;
if(opt == "+") {
ROF(i, N - 1, x) {
f[i] = (f[i] + f[i - x]) % P;
}
}
else {
FOR(i, x, N - 1) {
f[i] = (f[i] - f[i - x] + P) % P;
}
}
cout << f[k] << endl;
}
}
G - Electric Circuit \(\color{orange}{2439}\)
状压枚举不和其它点有连边的点集,
但是点集连变方案中有重复的,
需要枚举子集进行去重。
const int N = 17;
const int M = 1 << 17;
int n, m, ru[N], ch[N];
int sr[M], sc[M];
mint f[M], g[M], ans;
void solve() {
cin >> n >> m;
REP(i, m) {
int x; cin >> x; x--;
ru[x]++;
}
REP(i, m) {
int x; cin >> x; x--;
ch[x]++;
}
comb.init(m);
FOR(i, 1, (1 << n) - 1) {
REP(j, n) {
if(i & (1 << j)) {
sr[i] += ru[j];
sc[i] += ch[j];
}
}
if(sr[i] != sc[i]) continue;
f[i] = g[i] = comb.fac[sr[i]];
for(int j = i; j; j = (j - 1) & i) {
if(j & (-i & i)) {
f[i] -= f[j] * g[i ^ j];
}
}
ans += f[i] * comb.fac[m - sr[i]];
}
cout << ans / comb.fac[m] << endl;
}