比赛链接
卡系统栈的出题人应该拉出去祭天
A Skyscrapers
找出该行不同数的数量和数在该行的排名,那么这个数应该被赋值成行列排名的较大值,剩下的数往上延伸。主要排个序就行了。
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <tuple>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>
#ifdef LBT
#define LOG(FMT...) fprintf(stderr, FMT)
#else
#define LOG(FMT...)
#endif
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1010;
int n, m;
int a[N][N], x[N][N], y[N][N], r[N], c[N];
int tmp[N], res[N];
void grank(int n) {
static int st[N];
memcpy(st, tmp, sizeof(st));
sort(st + 1, st + n + 1);
int cnt = unique(st + 1, st + n + 1) - st - 1;
for (int i = 1; i <= n; ++i)
res[i] = lower_bound(st + 1, st + cnt + 1, tmp[i]) - st;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
scanf("%d", &a[i][j]);
for (int i = 1; i <= n; ++i) {
memcpy(tmp, a[i], sizeof(tmp));
grank(m);
memcpy(x[i], res, sizeof(res));
r[i] = *max_element(res + 1, res + m + 1);
}
for (int j = 1; j <= m; ++j) {
for (int i = 1; i <= n; ++i)
tmp[i] = a[i][j];
grank(n);
for (int i = 1; i <= n; ++i)
y[i][j] = res[i];
c[j] = *max_element(res + 1, res + n + 1);
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
int mx = max(x[i][j], y[i][j]);
mx += max(r[i] - x[i][j], c[j] - y[i][j]);
printf("%d ", mx);
}
putchar('\n');
}
return 0;
}
B Camp Schedule
应该尽量先填进去一串,然后是每次补尽量少的子使得串数 +1,那就应该补 KMP 跑出来的 的那段。算一下剩下的 1 够不够用。
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <tuple>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>
#ifdef LBT
#define LOG(FMT...) fprintf(stderr, FMT)
#else
#define LOG(FMT...)
#endif
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 500010;
int n, m;
int nxt[N];
char s[N], t[N];
int main() {
scanf("%s%s", s + 1, t + 1);
n = strlen(s + 1);
m = strlen(t + 1);
for (int i = 2; i <= m; ++i) {
int k = nxt[i - 1];
while (k && t[k + 1] != t[i])
k = nxt[k];
if (t[k + 1] == t[i])
++k;
nxt[i] = k;
}
int c1 = count(s + 1, s + n + 1, '1'), c2 = count(t + 1, t + m + 1, '1');
int cyc = m - nxt[m], cc = count(t + nxt[m] + 1, t + m + 1, '1');
int ans = 0;
for (int i = 1; i <= n; ++i) {
if (m + (i - 1) * cyc > n)
break;
ll res = n - (i - 1) * (ll)cyc - m, remain1 = c1 - cc * (ll)(i - 1) - c2;
if (remain1 < 0 || remain1 > res)
continue;
ans = i;
}
if (ans == 0)
puts(s + 1);
else {
printf("%s", t + 1);
--ans;
c1 -= c2;
n -= m;
while (ans--) {
printf("%s", t + nxt[m] + 1);
n -= cyc;
c1 -= cc;
}
while (c1--) {
putchar('1');
n--;
}
while (n--)
putchar('0');
}
return 0;
}
C Museums Tour
拆点成每个博物馆以及其在一个星期的第几天到达。只需注意到一个 SCC 如果能到达另一个 SCC,那么它们不可能包含同一个博物馆。因此答案不会重复。
,跑 500w 的 tarjan 要手写栈
在原图上跑一个 SCC 之后在 SCC 内通过拆点 BFS 就只可能递归 10w 了
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <tuple>
#include <stack>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>
#ifdef LBT
#define LOG(FMT...) fprintf(stderr, FMT)
#else
#define LOG(FMT...)
#endif
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 100010, D = 50;
int n, d, sc;
int dfn[N * D], low[N * D], good[N * D], belongs[N * D], nxt[N * D];
int ev[N], enxt[N];
bool instk[N * D], use[N];
char s[N][D];
int ans[N * D], seq[N * D];
int g[N];
int norm(int x) { return x >= d ? (x - d) : x; }
int gu(int u, int k) { return (u - 1) * d + k + 1; }
void tarjan();
int dfs2(int u);
void adde(int u, int v);
int main() {
int m;
scanf("%d%d%d", &n, &m, &d);
while (m--) {
int u, v;
scanf("%d%d", &u, &v);
adde(u, v);
}
for (int i = 1; i <= n; ++i) {
scanf("%s", s[i]);
}
tarjan();
memset(ans, -1, sizeof(ans));
for (int i = 1; i <= sc; ++i)
dfs2(seq[i]);
printf("%d\n", ans[1]);
return 0;
}
int dfs2(int u) {
ans[u] = 0;
for (int v = u; v; v = nxt[v]) {
for (int i = g[(v - 1) / d + 1]; i; i = enxt[i]) {
if (belongs[gu(ev[i], norm((v - 1) % d + 1))] != u)
ans[u] = max(ans[u], ans[belongs[gu(ev[i], norm((v - 1) % d + 1))]]);
}
}
return ans[u] += good[u];
}
void tarjan() {
static int t, top, fc;
static int stk[N * D];
static int fi[N * D], fuu[N * D];
fuu[++fc] = 1;
fi[fc] = -1;
while (fc) {
int uu = fuu[fc], u = (uu - 1) / d + 1, k = (uu - 1) % d;
int i = fi[fc];
if (i == -1) {
dfn[uu] = low[uu] = ++t;
stk[++top] = uu;
instk[uu] = true;
i = g[u];
} else {
low[uu] = min(low[uu], low[gu(ev[i], norm(k + 1))]);
i = enxt[i];
}
for (; i; i = enxt[i]) {
int vv = gu(ev[i], norm(k + 1));
if (!dfn[vv]) {
++fc;
fuu[fc] = vv;
fi[fc] = -1;
break;
} else if (instk[vv])
low[uu] = min(low[vv], low[uu]);
}
if (i) {
fi[fc - 1] = i;
continue;
}
if (dfn[uu] == low[uu]) {
int v;
int tmp = top;
do {
v = stk[top--];
instk[v] = false;
low[v] = dfn[uu];
belongs[v] = uu;
} while (uu != v);
int cost = 0;
for (int i = top + 1; i <= tmp; ++i) {
int v = stk[i];
int u = (v - 1) / d + 1, k = (v - 1) % d;
if (s[u][k] == '1') {
if (!use[u]) {
++cost;
use[u] = true;
}
}
if (i < tmp)
nxt[v] = stk[i + 1];
}
for (int i = top + 1; i <= tmp; ++i) {
int v = stk[i];
use[(v - 1) / d + 1] = false;
}
good[uu] = cost;
seq[++sc] = uu;
}
--fc;
}
}
void adde(int u, int v) {
static int t;
++t;
ev[t] = v;
enxt[t] = g[u];
g[u] = t;
}
D Cooperative Game
经典的 Floyd 判环法,先让一个人每次走 1 步,另一个人每次走 2 步,它们下次相遇在前者走
步后,则已经经过了
次操作。接下来所有人一起走,再经过
步后汇合与交点。总共不超过
次操作。
CF:只要我出的题够经典,以至于所有人都见过,那就不算原题
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <tuple>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>
#ifdef LBT
#define LOG(FMT...) fprintf(stderr, FMT)
#else
#define LOG(FMT...)
#endif
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int belongs[10];
void qry(const vector<int>& s) {
printf("next");
for (int v : s)
printf(" %d", v);
putchar('\n');
fflush(stdout);
int k;
scanf("%d", &k);
static char tmp[11];
while (k--) {
scanf("%s", tmp);
for (char* p = tmp; *p; ++p)
belongs[*p - '0'] = k;
}
}
int main() {
while (true) {
qry({0, 1});
qry({1});
if (belongs[0] == belongs[1])
break;
}
while (count(belongs, belongs + 10, 0) != 10)
qry({0, 1, 2, 3, 4, 5, 6, 7 ,8 ,9});
puts("done");
return 0;
}
E Train Car Selection
加减一次函数可以斜率优化,维护一个两边可以插入节点的凸包。
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <tuple>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>
#ifdef LBT
#define LOG(FMT...) fprintf(stderr, FMT)
#else
#define LOG(FMT...)
#endif
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef ll Num;
typedef long double BNum;
struct Vec {
Num x, y;
Vec() : x(), y() {}
Vec(Num x, Num y) : x(x), y(y) {}
bool operator<(const Vec &rhs) const {
return x < rhs.x;
}
Vec operator+(const Vec& rhs) const { return Vec(x + rhs.x, y + rhs.y); }
Vec operator-(const Vec& rhs) const { return Vec(x - rhs.x, y - rhs.y); }
BNum operator*(const Vec& rhs) const { return x * (BNum) rhs.y - y * (BNum) rhs.x; }
BNum operator^(const Vec& rhs) const { return x * (BNum) rhs.x + y * (BNum) rhs.y; }
};
const int N = 600010;
int l, r;
ll pos;
ll pl, pr;
ll lk, lb;
Vec hull[N];
ll gv(ll x, ll y) {
return x * lk + lb + y;
}
ll gv(const Vec& v) {
return gv(v.x, v.y);
}
int main() {
int m;
scanf("%lld%d", &pr, &m);
l = m;
r = m;
hull[l] = Vec(0, 0);
while (m--) {
int x, y;
scanf("%d%d", &x, &y);
if (x == 1) {
pl -= y;
Vec ins(pl, -lk * pl - lb);
while (r > l && (ins - hull[l + 1]) * (ins - hull[l]) >= 0)
++l;
hull[--l] = ins;
} else if (x == 2) {
Vec ins(pr, -lk * pr - lb);
pr += y;
while (r > l && (ins - hull[r - 1]) * (hull[r] - hull[r - 1]) >= 0)
--r;
hull[++r] = ins;
} else {
int z;
scanf("%d", &z);
lb += y - z * pl;
lk += z;
}
int ml = l, mr = r;
while (ml < mr) {
int mid = (ml + mr) >> 1;
if (gv(hull[mid]) > gv(hull[mid + 1]))
ml = mid + 1;
else
mr = mid;
}
printf("%lld %lld\n", hull[ml].x - pl + 1, gv(hull[ml]));
}
return 0;
}
F Matches Are Not a Child’s Play
注意到每次 up
操作时假设之前权值最大的节点是
,然后 up
了
,那么顺序上就是其他的相对顺序不变,把
路径上的点都变成最后弹出了。可以考虑每次修改就是将每个点维护其最后一次被哪个操作影响,用树状数组维护第
次操作还剩多少点以其为最后一次,以及最初的顺序中有那些点还被保留。用树链剖分维护连续段的话容易做到
。
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <functional>
#include <queue>
#include <map>
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
const int N = 200010;
struct E { int v; E* next; };
int n, cur = 1;
int ord[N], col[N], deg[N], fw1[N], fw2[N], beg[N];
int sub[N], pref[N], prt[N], dep[N], dfn[N], top[N], rev[N];
map<int, int> stk[N];
char opt[10];
E* g[N];
void adde(int u, int v) { static E pool[N * 2], *p = pool; p->v = v; p->next = g[u]; g[u] = p++; ++deg[u]; }
void dfs(int u) {
sub[u] = 1;
for (E* p = g[u]; p; p = p->next)
if (!sub[p->v]) {
prt[p->v] = u; dep[p->v] = dep[u] + 1; dfs(p->v);
sub[u] += sub[p->v];
pref[u] = sub[pref[u]] > sub[p->v] ? pref[u] : p->v;
}
}
void hld(int u) {
static int t;
if (!top[u]) top[u] = u;
dfn[u] = ++t; rev[t] = u;
if (pref[u]) {
top[pref[u]] = top[u]; hld(pref[u]);
for (E* p = g[u]; p; p = p->next)
if (!dfn[p->v]) hld(p->v);
} else {
stk[top[u]].insert(make_pair(dep[u], 1));
stk[top[u]].insert(make_pair(dep[top[u]] - 1, 0));
}
}
int lowBit(int k) { return k & -k; }
void ch(int* fw, int k, int x) { for (; k < N; k += lowBit(k)) fw[k] += x; }
int qry(int* fw, int k) { int ret = 0; for (; k; k -= lowBit(k)) ret += fw[k]; return ret; }
int dist(int u, int v) {
int ret = 0, x = top[u], y = top[v];
while (x != y) {
if (dep[x] > dep[y]) swap(u, v), swap(x, y);
ret += dep[v] - dep[y] + 1;
v = prt[y]; y = top[v];
}
return ret + abs(dep[u] - dep[v]) + 1;
}
int when(int u) {
int c = stk[top[u]].lower_bound(dep[u])->second;
if (c == 1) return qry(fw1, ord[u]);
return qry(fw2, c - 1) + dist(u, beg[c]);
}
void clr(int u, int d) {
while (dep[u] >= d) {
ch(fw1, ord[u], -1);
u = prt[u];
}
}
void replace(int t, int l, int r, int c) {
map<int, int>& seg = stk[t];
map<int, int>::iterator it = seg.lower_bound(r);
bool f = false;
while (true) {
int cc = it->second;
ch(fw2, cc, prev(it)->first - r);
if (cc == 1)
clr(rev[dfn[t] + r - dep[t]], max(prev(it)->first + 1, l));
if (it->first == r) it->second = c;
else it = seg.insert(make_pair(r, c)).first;
if (!f)
r = (--it)->first;
else {
seg.erase(it--);
r = it->first;
}
if (r < l) {
if (r < l - 1) {
seg.insert(make_pair(l - 1, cc));
ch(fw2, cc, l - r - 1);
}
return;
}
f = true;
}
}
void replace(int u, int v, int c) {
int x = top[u], y = top[v];
while (x != y) {
if (dep[x] > dep[y]) swap(u, v), swap(x, y);
replace(y, dep[y], dep[v], c);
v = prt[y]; y = top[v];
}
if (dep[u] > dep[v]) swap(u, v);
replace(x, dep[u], dep[v], c);
}
int main() {
int q; scanf("%d%d", &n, &q);
for (int rep = 1; rep < n; ++rep) {
int u, v; scanf("%d%d", &u, &v);
adde(u, v); adde(v, u);
}
priority_queue<int, vector<int>, greater<int> > pq;
for (int i = 1; i <= n; ++i) if (deg[i] == 1) pq.push(i);
int t = 0;
while (!pq.empty()) {
int u = pq.top(); pq.pop();
ord[u] = ++t;
for (E* p = g[u]; p; p = p->next)
if (!ord[p->v] && --deg[p->v] == 1)
pq.push(p->v);
}
dfs(1); hld(1);
for (int i = 1; i <= n; ++i) ch(fw1, i, 1);
ch(fw2, 1, n);
int bef = n;
dep[0] = -1;
while (q--) {
int x, y; scanf("%s%d", opt, &x);
if (opt[0] == 'u') {
if (x == bef) continue;
beg[++cur] = bef;
ch(fw2, cur, dist(bef, x));
replace(x, bef, cur);
bef = x;
} else if (opt[0] == 'w') {
printf("%d\n", when(x));
} else {
scanf("%d", &y);
printf("%d\n", when(x) < when(y) ? x : y);
}
}
return 0;
}