A. Exponential Plant
模拟
代码实现
h = int(input())
now, day = 0, 0
while now <= h:
now += 1<<day
day += 1
print(day)
B. AtCoder Janken 2
模拟
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<pair<string, int>> user;
int t = 0;
rep(i, n) {
string s; int c;
cin >> s >> c;
user.emplace_back(s, c);
t += c;
}
sort(user.begin(), user.end());
string ans = user[t%n].first;
cout << ans << '\n';
return 0;
}
C. AtCoder Magics
其实就是找二维平面上以 \(a_i\) 最大的点结尾的最长严格上升点列
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n), c(n);
rep(i, n) cin >> a[i] >> c[i];
vector<int> is(n);
iota(is.begin(), is.end(), 0);
sort(is.begin(), is.end(), [&](int i, int j) {
return a[i] > a[j];
});
vector<int> ans;
for (int i : is) {
if (!ans.size() or c[ans.back()] > c[i]) {
ans.push_back(i);
}
}
sort(ans.begin(), ans.end());
cout << ans.size() << '\n';
for (int i : ans) cout << i+1 << ' ';
return 0;
}
E. Remove Pairs
状压dp
记 dp[S]
表示状态为从剩下的卡片集合为 \(S\) 开始的胜负
博弈论的基本性质:
- 一个状态是必败状态,当且仅当它的所有后继都是必胜状态
- 一个状态是必胜状态,当且仅当它至少有一个后继是必败状态
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n), b(n);
rep(i, n) cin >> a[i] >> b[i];
int n2 = 1<<n;
vector<bool> dp(n2);
rep(s, n2) {
bool now = false;
rep(i, n)rep(j, i) if ((s>>i&1) and (s>>j&1)) {
if (a[i] == a[j] or b[i] == b[j]) {
if (!dp[s^1<<i^1<<j]) now = true;
}
}
dp[s] = now;
}
if (dp[n2-1]) puts("Takahashi");
else puts("Aoki");
return 0;
}
F. Useless for LIS
可以先求出以每个数结尾的 LIS 的长度
用线段树优化dp可以做到 \(\mathcal{O}(n\log n)\)
那么如何判断一个数出现在整个序列的 LIS 里呢?
- 可以先求出以每个数 \(x\) 开始的 LIS 的长度
- 然后判断以 \(x\) 结尾的 LIS 的长度 + 以 \(x\) 开始的 LIS 的长度是否等于整个序列的 LIS 的长度 - 1
代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
// Coodinate Compression
template<typename T=int>
struct CC {
bool initialized;
vector<T> xs;
CC(): initialized(false) {}
void add(T x) { xs.push_back(x);}
void init() {
sort(xs.begin(), xs.end());
xs.erase(unique(xs.begin(),xs.end()),xs.end());
initialized = true;
}
int operator()(T x) {
if (!initialized) init();
return upper_bound(xs.begin(), xs.end(), x) - xs.begin() - 1;
}
T operator[](int i) {
if (!initialized) init();
return xs[i];
}
int size() {
if (!initialized) init();
return xs.size();
}
};
int op(int a, int b) { return max(a, b); }
int e() { return 0; }
void solve() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
{
CC cc;
rep(i, n) cc.add(a[i]);
rep(i, n) a[i] = cc(a[i]);
}
auto get = [&]() {
vector<int> dp(n);
segtree<int, op, e> t(n);
rep(i, n) {
dp[i] = t.prod(0, a[i])+1;
t.set(a[i], dp[i]);
}
return dp;
};
vector<int> dl = get();
rep(i, n) a[i] = n-1-a[i];
reverse(a.begin(), a.end());
vector<int> dr = get();
reverse(dr.begin(), dr.end());
int lis = *max_element(dl.begin(), dl.end());
vector<int> ans;
rep(i, n) {
if (dl[i]+dr[i]-1 == lis) ans.push_back(i);
}
cout << ans.size() << '\n';
for (int i : ans) cout << i+1 << ' ';
cout << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
G. Select Strings
考虑如果 \(S_j\) 是 \(S_i\) 的子串,则将点 \(i\) 向点 \(j\) 连一条有向边,特别地,如果 \(S_i = S_j\),直接跳过,可以发现最后得到的图是一个DAG
我们需要求的就是这个图的最大反链,而最大反链 \(=\) 最小链覆盖
对于最小链覆盖是网络流的经典模型:拆点建二分图,原图的最小链覆盖就是 \(N-\) 二分图的最大匹配,其中 \(N = \sum A_i\)
另外,对于出现 \(S_i\) 和 \(S_j\) 相同的情况,可以考虑取这个字符串对应的 \(\max (A)\)
代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n;
cin >> n;
vector<string> s;
vector<int> a;
{
vector<string> S(n);
rep(i, n) cin >> S[i];
map<string, int> mx;
rep(i, n) {
int na;
cin >> na;
mx[S[i]] = max(mx[S[i]], na);
}
for (auto [ns, na] : mx) {
s.push_back(ns);
a.push_back(na);
}
n = s.size();
}
int sv = n*2, tv = sv+1;
mf_graph<ll> g(tv+1);
const ll INF = 1e18;
rep(i, n)rep(j, n) if (i != j) {
if (s[i].find(s[j]) != string::npos) {
g.add_edge(i, n+j, INF);
}
}
rep(i, n) g.add_edge(sv, i, a[i]);
rep(i, n) g.add_edge(n+i, tv, a[i]);
ll ans = 0;
rep(i, n) ans += a[i];
ans -= g.flow(sv, tv);
cout << ans << '\n';
return 0;
}