E - MEX
简单的统计 - AC
各种M => 各种ME => 各种MEX => ANS.
刚开始把题目看错了,以为无序。
int get(char ch) {
if (ch == 'M') return 0;
if (ch == 'E') return 1;
return 2;
}
int mex(int i, int j, int k) {
ll res = 0;
if (!i || !j || !k) {
++res;
if (i == 1 || j == 1 || k == 1) {
++res;
if (i == 2 || j == 2 || k == 2) {
++res;
}
}
}
return res;
}
int main() {
setIO("");
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
cin >> str;
for (int i = 1; i <= n; ++i) {
int k = get(str[i - 1]);
if (!k) {
++c[0][a[i]];
}
else if (k == 1) {
for (int j = 0; j < 3; ++j) {
c[1][a[i] * 3 + j] += c[0][j];
}
}
else {
for (int j = 0; j < 9; ++j) {
c[2][a[i] * 9 + j] += c[1][j];
}
}
}
for (int i = 0; i < 27; ++i) {
int z = i;
int x = z / 9; z %= 9;
int y = z / 3; z %= 3;
ans += c[2][i] * mex(x, y, z);
}
cout << ans << '\n';
return 0;
}
考虑E (补)
对于某个E,在MEX中。所以预处理前i个数中各M个数,后i个数中个X个数,类似于合唱队型。
F - Vouchers
贪心 - AC
从大到小排序,能用的大票一定会用。
struct Node {
ll l, d;
bool operator < (const Node &x) const {
return d > x.d;
}
} a[MAXM];
int n, m;
ll ans;
multiset<ll> s;
int main() {
setIO("");
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
ll p; cin >> p; ans += p;
s.insert(p);
}
for (int i = 1; i <= m; ++i) {
cin >> a[i].l;
}
for (int i = 1; i <= m; ++i) {
cin >> a[i].d;
}
sort(a + 1, a + m + 1);
for (int i = 1; i <= m; ++i) {
auto it = s.lower_bound(a[i].l);
if (it != s.end()) {
s.erase(it);
ans -= a[i].d;
}
}
cout << ans << '\n';
return 0;
}
G - Minimum Xor Pair Query
Trie
尝试01Trie.
插入和删除解决。
查询,在Trie上走,如果从当前节点u到下一个节点v,从v往下还有至少两个数,那么为了让异或值最小,一定不会让数对中的两个数从u点分岔。
当u到v1,v2,v1,v2往下走分别只有一个数,那么只能分岔,并且此时结果一定。
画成树的形式,可以看到,两个数一定是“相邻”的两个叶子节点。
但是,两个数不分岔时,依旧有两种情况:一起走1、一起走2.
也许插入和删除时可以树形dp维护答案:两个数的异或只能在分岔口处实现,所以针对每个节点,记录它往下走的一个数(实际上可能有多个数,但对答案有贡献时,一定只有一个数),应该可做,但我没有写出来。
如果没有删除操作,也很好做:插入一个数之前,带它走一遍Trie,得到一个可能的结果,与答案打擂台。但是要删除一个数时,如果正好删除了最小值,那么跟上来的会是什么,不得而知。
STL multiset - AC (补)
画成树的形式,可以看到,两个数一定是“相邻”的两个叶子节点。
这也是在说,黑板上的数从小到大排序,数对中的两个数一定相邻。
这意味着Trie已经失去了它的优越性:给定x,找到与x配对的数y.
但是要删除一个数时,如果正好删除了最小值,那么跟上来的会是什么,不得而知。
将可能的结果放到STL multiset中就好。当时想到了这一点,但觉得太不可能。
int q;
multiset<int> s, res;
void del(int x) {
auto it = res.find(x);
res.erase(it);
}
int main() {
setIO("");
cin >> q;
s.insert(-1); s.insert(MAXX);
while (q--) {
int op, x; cin >> op;
if (op == 1) {
cin >> x;
auto it = s.insert(x);
auto pre = prev(it), nxt = next(it);
if (*pre > -1 && *pre < MAXX) res.insert(*pre ^ x);
if (*nxt > -1 && *nxt < MAXX) res.insert(*nxt ^ x);
if (*pre > -1 && *pre < MAXX && *nxt > -1 && *nxt < MAXX) res.erase(res.find(*pre ^ *nxt));
}
else if (op == 2) {
cin >> x;
auto it = s.find(x);
auto pre = prev(it), nxt = next(it);
if (*pre > -1 && *pre < MAXX) res.erase(res.find(*pre ^ x));//erase(value)会删除所有value.
if (*nxt > -1 && *nxt < MAXX) res.erase(res.find(*nxt ^ x));
if (*pre > -1 && *pre < MAXX && *nxt > -1 && *nxt < MAXX) res.insert(*pre ^ *nxt);
s.erase(it);
}
else {
cout << *res.begin() << '\n';
}
}
return 0;
}
Ex - Make Q
最小生成树
在树上加一条边可以构成环。又要cost最小,想到可能要在最小生成树的基础上。
但是此处Q不在意节点个数,只在意边权大小;最小生成树则需要顾及所有节点。
举例发现最小生成树确实不太适合本题。
枚举
N ≤ 300 N\leq 300 N≤300,支持 O ( N 3 ) O(N^3) O(N3),可能要先枚举某个东西(有点假期计划的感觉。枚举边吗?枚举点吗?思路往后就乱了,迷糊了,没有想出来。
要找出所有环吗?不可能,环的个数可达到 2 n 2^n 2n的数量级.
枚举、最短路径树 - (补)
边太多,枚举点。枚举最特殊的、最有性质的那个点:环上的、与Q的尾巴相连的点,记为a。
对于环的寻找,枚举环上相邻的两点b-c,即环为a----------b-c---------a,还需要知道a到b、a到c的最短距离。在剩下的不在环上的与a相邻的点中找一个与a距离最短的点d,构成尾巴。
dijkstra求最短路时,会构成一棵树,保证从根到节点的路径恰是一条最短路径。利用这棵树,可以找出点d,它是根的直接子节点之一(易证,注意与a相邻的点在树中不一定是a的子节点)。这棵树被称作“最短路径树”。
时间复杂度
O
(
n
3
)
O(n^3)
O(n3).
写不对,不知道为什么。先放着。
int n, m, g[MAXN][MAXN], dis[MAXN], top[MAXN], ans;
bool vis[MAXN];
void dijkstra(int beg) {
memset(vis, 0, sizeof(vis)); vis[beg] = true;
memset(dis, 0x3f, sizeof(dis)); dis[beg] = 0;
top[beg] = 0;
for (int i = 1; i <= n; ++i) {
if (g[beg][i]) dis[i] = g[beg][i], top[i] = i;
}
for (int i = 1; i < n; ++i) {
int pos = -1;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && (pos == -1 || dis[j] < dis[pos])) pos = j;
}
vis[pos] = true;
for (int j = 1; j <= n; ++j) {
if (g[pos][j] && ckmin(dis[j], dis[pos] + g[pos][j])) top[j] = top[pos];
}
}
}
int main() {
setIO("");
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int a, b, c; cin >> a >> b >> c;
g[a][b] = g[b][a] = c;
}
ans = INF;
for (int i = 1; i <= n; ++i) {
dijkstra(i);
int m[3] = { INF, INF, INF };
for (int j = 1; j <= n; ++j) {
if (j != i && top[j] == j) {
if (m[0] >= g[i][j]) m[2] = m[1], m[1] = m[0], m[0] = g[i][j];
else if (m[1] >= g[i][j]) m[2] = m[1], m[1] = g[i][j];
else if (m[2] > g[i][j]) m[2] = g[i][j];
}
}
for (int j = 1; j <= n; ++j) {
if (j == i) continue;
for (int k = j + 1; k <= n; ++k) {
if (k != i && top[j] != top[k] && g[j][k]) {
int x = g[i][top[j]], y = g[i][top[k]], tmp = m[0];
if (m[0] == x && m[1] == y || m[0] == y && m[1] == x) tmp = m[2];
else if (m[0] == x || m[0] == y) tmp = m[1];
ckmin(ans, dis[j] + dis[k] + g[j][k] + tmp);
}
}
}
}
if (ans < INF) cout << ans << '\n';
else cout << -1 << '\n';
return 0;
}