Codeforces 634F Orchestra
若枚举矩形的上下边界,则问题显然可以 O ( n ) O(n) O(n) 解决。
那么,考虑枚举矩形的上边界,再从上到下枚举矩形的下边界,则我们需要一边插入点,一边动态维护上面问题的答案。若直接实现,需要使用平衡树。
事实上,我们可以从下到上枚举矩形的下边界,则只需要链表就可以维护了。
时间复杂度 O ( r 2 + r n k ) O(r^2+rnk) O(r2+rnk) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3005;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int r, c, n, k, home[MAXN];
int x[MAXN], y[MAXN], pre[MAXN], nxt[MAXN];
vector <int> a[MAXN];
bool cmp(int a, int b) {
return y[a] < y[b];
}
int main() {
read(r), read(c), read(n), read(k);
for (int i = 1; i <= n; i++) {
read(x[i]), read(y[i]), home[i] = i;
a[x[i]].push_back(i);
}
y[n + 1] = 0, y[n + 2] = c + 1;
sort(home + 1, home + n + 1, cmp);
home[0] = n + 1, home[n + 1] = n + 2;
ll ans = r * (r + 1ll) * c * (c + 1ll) / 4;
for (int p = 1; p <= r; p++) {
for (int i = 1; i <= n + 1; i++) {
pre[home[i]] = home[i - 1];
nxt[home[i - 1]] = home[i];
}
ll sum = 0;
for (int i = n + 1; i != n + 2; i = nxt[i]) {
int len = y[nxt[i]] - y[i];
sum += (len - 1ll) * len / 2;
for (int j = nxt[i], cnt = 1; j != n + 2 && cnt <= k - 1; j = nxt[j], cnt++)
sum += len * (y[nxt[j]] - y[j]);
}
pre[n + 1] = n + 1, nxt[n + 2] = n + 2;
for (int i = 1; i <= p - 1; i++) {
for (auto x : a[i]) {
int pos = x;
for (int j = 1; j <= k; j++)
pos = pre[pos];
int qos = pos;
for (int j = 1; j <= k; j++)
qos = nxt[qos];
while (pos != x && qos != n + 2) {
sum += (y[nxt[pos]] - y[pos]) * (y[nxt[qos]] - y[qos]);
pos = nxt[pos];
qos = nxt[qos];
}
int p = pre[x], s = nxt[x];
nxt[p] = s, pre[s] = p;
}
}
for (int i = r; i >= p; i--) {
ans -= sum;
for (auto x : a[i]) {
int pos = x;
for (int j = 1; j <= k; j++)
pos = pre[pos];
int qos = pos;
for (int j = 1; j <= k; j++)
qos = nxt[qos];
while (pos != x && qos != n + 2) {
sum += (y[nxt[pos]] - y[pos]) * (y[nxt[qos]] - y[qos]);
pos = nxt[pos];
qos = nxt[qos];
}
int p = pre[x], s = nxt[x];
nxt[p] = s, pre[s] = p;
}
}
}
cout << ans << endl;
return 0;
}
Codeforces 639E Bear and Paradox
考虑确定最优策略,由调整法,可以证明, i i i 应当排在 j j j 之前当且仅当 p j t i < p i t j p_jt_i<p_it_j pjti<pitj ;而对于 p j t i = p i t j p_jt_i=p_it_j pjti=pitj , i , j i,j i,j 的顺序是任意的。
那么,二分答案,考虑判断答案 c c c 是否合法。
则要求对于分数 x , y ( x < y ) x,y\ (x<y) x,y (x<y) ,分数为 x x x 的题目可能的最大分数不超过分数为 y y y 的题目的最小分数,不难发现只要检查相邻的分数之间的偏序关系就可以了。
时间复杂度 O ( N L o g V ) O(NLogV) O(NLogV) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const double eps = 1e-10;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
double sumt, pre[MAXN]; int Min[MAXN], Max[MAXN];
int n, a[MAXN], b[MAXN], p[MAXN], t[MAXN];
bool cmp(int x, int y) {
return p[x] < p[y];
}
bool cnp(int x, int y) {
return 1ll * t[x] * p[y] < 1ll * t[y] * p[x];
}
bool check(double c) {
double last;
for (int i = 1, nxt; i <= n; i = nxt + 1) {
nxt = i; while (nxt + 1 <= n && p[b[i]] == p[b[nxt + 1]]) nxt++;
if (i != 1) {
for (int j = i; j <= nxt; j++) {
double cur = p[b[j]] * (1 - c * pre[Max[b[j]]] / sumt);
if (cur < last) return false;
}
}
last = 0;
for (int j = i; j <= nxt; j++)
chkmax(last, p[b[j]] * (1 - c * (pre[Min[b[j]] - 1] + t[b[j]]) / sumt));
}
return true;
}
int main() {
read(n);
for (int i = 1; i <= n; i++)
a[i] = b[i] = i;
for (int i = 1; i <= n; i++)
read(p[i]);
for (int i = 1; i <= n; i++)
read(t[i]), sumt += t[i];
sort(a + 1, a + n + 1, cnp);
sort(b + 1, b + n + 1, cmp);
for (int i = 1; i <= n; i++)
pre[i] = pre[i - 1] + t[a[i]];
for (int i = 1, nxt; i <= n; i = nxt + 1) {
nxt = i; while (nxt + 1 <= n && 1ll * p[a[i]] * t[a[nxt + 1]] == 1ll * t[a[i]] * p[a[nxt + 1]]) nxt++;
for (int j = i; j <= nxt; j++) {
Min[a[j]] = i;
Max[a[j]] = nxt;
}
}
double l = 0, r = 1;
while (l + eps < r) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%.10lf\n", (l + r) / 2);
return 0;
}
Codeforces 639F Bear and Chemistry
首先,题目的要求等价于给定的点集在同一边双连通分量内。
将给定的原图缩点,则剩余部分是一片森林。
对于每个询问,将涉及到的点取出,建立虚树,再在虚树上运行 Tarjan 计算答案即可。
时间复杂度 O ( N + M + ∑ ( n i + m i ) L o g N ) O(N+M+\sum(n_i+m_i)LogN) O(N+M+∑(ni+mi)LogN) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
const int MAXLOG = 20;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
vector <int> a[MAXN], b[MAXN], c[MAXN], d[MAXN];
int n, m, q, f[MAXN], x[MAXN], y[MAXN];
int father[MAXN][MAXLOG], depth[MAXN];
int timer, dfn[MAXN], cnt, num[MAXN];
bool instack[MAXN]; ll online;
int Timer, Dfn[MAXN], Low[MAXN];
int top, Stack[MAXN], tot, belong[MAXN];
void tarjan(int pos, int fa) {
Low[pos] = Dfn[pos] = ++Timer;
Stack[++top] = pos;
instack[pos] = true;
for (unsigned i = 0; i < c[pos].size(); i++) {
if (d[pos][i] == fa) continue;
if (Dfn[c[pos][i]] == 0) {
tarjan(c[pos][i], d[pos][i]);
Low[pos] = min(Low[pos], Low[c[pos][i]]);
} else if (instack[c[pos][i]]) Low[pos] = min(Low[pos], Dfn[c[pos][i]]);
}
if (Low[pos] == Dfn[pos]) {
int tmp = Stack[top--];
belong[tmp] = ++tot;
instack[tmp] = false;
while (tmp != pos) {
tmp = Stack[top--];
belong[tmp] = tot;
instack[tmp] = false;
}
}
}
int find(int x) {
if (f[x] == x) return x;
else return f[x] = find(f[x]);
}
int lca(int x, int y) {
if (depth[x] < depth[y]) swap(x, y);
for (int i = MAXLOG - 1; i >= 0; i--)
if (depth[father[x][i]] >= depth[y]) x = father[x][i];
if (x == y) return x;
for (int i = MAXLOG - 1; i >= 0; i--)
if (father[x][i] != father[y][i]) {
x = father[x][i];
y = father[y][i];
}
return father[x][0];
}
void addedge(int x, int y) {
static int ind = 0; ind++;
c[x].push_back(y);
d[x].push_back(ind);
c[y].push_back(x);
d[y].push_back(ind);
}
inline int func(int x) {
x = (x + online) % n;
if (x == 0) x = n;
return x;
}
void solve(int task) {
static int vis[MAXN], points[MAXN];
static int pos[MAXN], x[MAXN], y[MAXN];
int n, m, cnt = 0; read(n), read(m);
auto mark = [&] (int x) {
if (vis[x] != task) {
vis[x] = task;
points[++cnt] = x;
}
if (vis[num[x]] != task) {
vis[num[x]] = task;
points[++cnt] = num[x];
}
};
for (int i = 1; i <= n; i++) {
read(pos[i]), pos[i] = func(pos[i]);
mark(pos[i] = find(pos[i]));
}
for (int i = 1; i <= m; i++) {
read(x[i]), read(y[i]);
x[i] = func(x[i]);
y[i] = func(y[i]);
mark(x[i] = find(x[i]));
mark(y[i] = find(y[i]));
}
sort(points + 1, points + cnt + 1, [&] (int x, int y) {return dfn[x] < dfn[y];});
int top = 0, cmt = cnt; static int Stack[MAXN];
for (int i = 1; i <= cnt; i++) {
int x = points[i];
if (num[points[i - 1]] != num[points[i]]) {
while (top >= 2) {
addedge(Stack[top], Stack[top - 1]);
top--;
}
Stack[top = 1] = x;
} else {
while (top >= 2 && depth[lca(x, Stack[top])] <= depth[Stack[top - 1]]) {
addedge(Stack[top], Stack[top - 1]);
top--;
}
if (lca(x, Stack[top]) == Stack[top]) Stack[++top] = x;
else {
int y = lca(x, Stack[top]);
points[++cmt] = y;
addedge(Stack[top], y);
Stack[top] = y;
Stack[++top] = x;
}
}
}
while (top >= 2) {
addedge(Stack[top], Stack[top - 1]);
top--;
}
for (int i = 1; i <= m; i++)
addedge(x[i], y[i]);
for (int i = 1; i <= cnt; i++)
if (Dfn[points[i]] == 0) tarjan(points[i], 0);
bool ans = true;
for (int i = 1; i <= n; i++)
ans &= belong[pos[i]] == belong[pos[1]];
if (ans) puts("YES"), online += task;
else puts("NO");
tot = Timer = 0;
for (int i = 1; i <= cmt; i++) {
c[points[i]].clear();
d[points[i]].clear();
Dfn[points[i]] = 0;
}
}
void dfs(int pos, int fa) {
father[pos][0] = fa;
depth[pos] = depth[fa] + 1;
for (auto x : a[pos])
if (x != fa) dfs(x, pos);
}
void work(int pos, int fa, int cur) {
depth[pos] = depth[fa] + 1;
father[pos][0] = fa;
dfn[pos] = ++timer, num[pos] = cur;
for (int i = 1; i < MAXLOG; i++)
father[pos][i] = father[father[pos][i - 1]][i - 1];
for (unsigned i = 0; i < b[pos].size(); i++)
if (b[pos][i] != fa) work(b[pos][i], pos, cur);
}
int main() {
read(n), read(m), read(q);
for (int i = 1; i <= n; i++)
f[i] = i;
static bool non[MAXN];
for (int i = 1; i <= m; i++) {
read(x[i]), read(y[i]);
int tx = find(x[i]);
int ty = find(y[i]);
if (tx == ty) non[i] = true;
else {
f[tx] = ty;
a[x[i]].push_back(y[i]);
a[y[i]].push_back(x[i]);
}
}
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 1; i <= n; i++)
if (father[i][0] == 0) dfs(i, 0);
for (int i = 1; i <= m; i++)
if (non[i]) {
int tx = find(x[i]), ty = find(y[i]);
while (tx != ty) {
if (depth[tx] < depth[ty]) swap(tx, ty);
f[tx] = find(father[tx][0]), tx = find(tx);
}
}
for (int i = 1; i <= n; i++)
for (auto x : a[i]) if (find(i) != find(x)) b[find(i)].push_back(find(x));
for (int i = 1; i <= n; i++)
if (find(i) == i && dfn[i] == 0) {
work(i, 0, i);
}
for (int i = 1; i <= q; i++)
solve(i);
return 0;
}
Codeforces 666D Chain Reaction
首先枚举各个机器人与正方形的四个角落的对应关系。
若四个机器人移动的横竖方向一致,则我们可以确定正方形的边长 L L L ,问题即为给定 a 1 , a 2 , a 3 , a 4 a_1,a_2,a_3,a_4 a1,a2,a3,a4 ,选取 x x x ,最小化 m a x { ∣ a i + [ i ≤ 2 ] L − x ∣ } max\{|a_i+[i\leq 2]L-x|\} max{
∣ai+[i≤2]L−x∣} 。
令 a 1 = a 1 + L , a 2 = a 2 + L a_1=a_1+L,a_2=a_2+L a1