Codeforces 528C Data Center Drama
考虑存在解的必要条件,显然,各个点的度数应为偶数,且 也应为偶数。
任意一张满足以上条件的图都存在一条长度为偶数的欧拉回路,取路上奇数位的边为正向,偶数位的边为反向即可构造一组解法。
因此,任意用最少的步数将图改造为满足以上条件的图即可。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
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;
}
bool vis[MAXN];
unsigned cur[MAXN];
vector <int> ans, points;
vector <pair <int, int>> a[MAXN];
void work(int pos) {
for (unsigned &i = cur[pos]; i < a[pos].size(); i++)
if (!vis[a[pos][i].second]) {
vis[a[pos][i].second] = true;
work(a[pos][i].first);
}
ans.push_back(pos);
}
int main() {
int n, m; read(n), read(m);
for (int i = 1; i <= m; i++) {
int x, y; read(x), read(y);
a[x].emplace_back(y, i);
a[y].emplace_back(x, i);
}
vector <int> points;
for (int i = 1; i <= n; i++)
if (a[i].size() & 1) points.push_back(i);
while (!points.empty()) {
int x = points.back(); points.pop_back();
int y = points.back(); points.pop_back(), m++;
a[x].emplace_back(y, m);
a[y].emplace_back(x, m);
}
if (m & 1) {
m++;
a[1].emplace_back(1, m);
a[1].emplace_back(1, m);
}
printf("%d\n", m); work(1);
for (int i = 1; i <= m; i++)
if (i & 1) printf("%d %d\n", ans[i - 1], ans[i]);
else printf("%d %d\n", ans[i], ans[i - 1]);
return 0;
}
Codeforces 536D Tavas in Kansas
首先求出最短路,并离散化。
不难得到动态规划解法,记 表示仅考虑到 距离在 以上,到 距离在 以上的点,先手 / 后手玩家能够取到的最优分差,转移显然可以枚举下一步如何行动。
观察转移,用前缀和优化之即可。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e3 + 5;
const long long INF = 1e18;
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;
}
namespace ShortestPath {
const ll INF = 1e18;
const int MAXP = 1e6;
struct edge {int dest, len; };
int n; ll dist[MAXP];
vector <edge> a[MAXP];
set <pair <ll, int> > st;
void addedge(int x, int y, int z) {
a[x].push_back((edge) {y, z});
}
void init(int x) {
n = x; st.clear();
for (int i = 1; i <= n; i++) {
dist[i] = INF;
a[i].clear();
}
}
void work(int s) {
dist[s] = 0;
for (int i = 1; i <= n; i++)
if (s != i) dist[i] = INF;
st.insert(make_pair(0, s));
while (!st.empty()) {
pair <ll, int> tmp = *st.begin();
st.erase(tmp);
for (unsigned i = 0; i < a[tmp.second].size(); i++) {
int dest = a[tmp.second][i].dest;
ll newlen = tmp.first + a[tmp.second][i].len;
if (newlen < dist[dest]) {
st.erase(make_pair(dist[dest], dest));
dist[dest] = newlen;
st.insert(make_pair(dist[dest], dest));
}
}
}
}
}
int n, m, s, t;
int p[MAXN], x[MAXN], y[MAXN];
int cnt[MAXN][MAXN]; ll sum[MAXN][MAXN];
ll dp[MAXN][MAXN][2], aux[MAXN][MAXN][2];
void makecor(ll *x, int *y) {
set <ll> st; map <ll, int> res;
for (int i = 1; i <= n; i++)
st.insert(x[i]);
int tot = 0;
for (auto x : st) res[x] = ++tot;
for (int i = 1; i <= n; i++)
y[i] = res[x[i]];
}
int calcnt(int lx, int rx, int ly, int ry) {
return cnt[rx][ry] - cnt[rx][ly - 1] - cnt[lx - 1][ry] + cnt[lx - 1][ly - 1];
}
ll calsum(int lx, int rx, int ly, int ry) {
return sum[rx][ry] - sum[rx][ly - 1] - sum[lx - 1][ry] + sum[lx - 1][ly - 1];
}
void debug() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
printf("(%4lld,%4lld) ", dp[i][j][0], dp[i][j][1]);
printf("\n");
}
}
int main() {
read(n), read(m), read(s), read(t);
for (int i = 1; i <= n; i++)
read(p[i]);
ShortestPath :: init(n);
for (int i = 1; i <= m; i++) {
int x, y, z;
read(x), read(y), read(z);
ShortestPath :: addedge(x, y, z);
ShortestPath :: addedge(y, x, z);
}
ShortestPath :: work(s);
makecor(ShortestPath :: dist, x);
ShortestPath :: work(t);
makecor(ShortestPath :: dist, y);
for (int i = 1; i <= n; i++) {
cnt[x[i]][y[i]]++;
sum[x[i]][y[i]] += p[i];
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
cnt[i][j] += cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1];
sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
}
for (int i = n; i >= 1; i--)
for (int j = n; j >= 1; j--) {
aux[i][j][0] = dp[i + 1][j][1] + sum[i][n] - sum[i][j - 1];
if (i != n) chkmax(aux[i][j][0], aux[i + 1][j][0]);
if (calcnt(i, i, j, n) == 0) dp[i][j][0] = dp[i + 1][j][0];
else dp[i][j][0] = aux[i][j][0] + sum[i - 1][j - 1] - sum[i - 1][n];
aux[i][j][1] = dp[i][j + 1][0] - sum[n][j] + sum[i - 1][j];
if (j != n) chkmin(aux[i][j][1], aux[i][j + 1][1]);
if (calcnt(i, n, j, j) == 0) dp[i][j][1] = dp[i][j + 1][1];
else dp[i][j][1] = aux[i][j][1] - sum[i - 1][j - 1] + sum[n][j - 1];
}
//debug();
if (dp[1][1][0] > 0) puts("Break a heart");
else if (dp[1][1][0] == 0) puts("Flowers");
else puts("Cry");
return 0;
}
Codeforces 538G Berserk Robot
首先,令对于点
,令
。
并令
。
旋转坐标系后,可以认为 两维是分立的,我们只需要分别解决一维的问题就可以了。
以 坐标为例,令 表示串中前 个元素的前缀和,则每条限制可以被描述为 。并且, 需要额外满足 。
那么,考虑相邻的两个有限制的 ,则有不等式 ,可以解出一个 的范围。取所有限制的并,任取一个限制内的 即可。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXL = 2e6 + 5;
const long long INF = 4e18;
typedef long long ll;
template <typename T> T abs(T x) {if (x <= 0) return -x; else return x; }
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 n, l; bool vis[MAXL];
ll t[MAXN], x[MAXN], y[MAXN];
pair <ll, ll> px[MAXL], py[MAXL];
ll solve(pair <ll, ll> x, pair <ll, ll> y) {
if ((y.first - x.first) % (y.second - x.second) == 0)
return (y.first - x.first) / (y.second - x.second);
puts("NO"), exit(0);
return -1;
}
ll Ceil(ll x, ll y) {
ll tmp = INF / y;
return (x + tmp * y) / y - tmp + 1;
}
ll Round(ll x, ll y) {
ll tmp = INF / y;
return (x + tmp * y) / y - tmp;
}
pair <ll, ll> calc(pair <ll, ll> x, pair <ll, ll> y, ll k) {
if (y.second == x.second) {
ll delta = abs(x.first - y.first);
if (delta <= k) return make_pair(-l, l);
else return make_pair(INF, -INF);
}
if (y.second < x.second) swap(x, y);
ll coef = y.second - x.second, delta = y.first - x.first;
ll Min = delta - k, Max = delta + k; pair <ll, ll> ans;
if (Min % coef == 0) ans.first = Min / coef;
else ans.first = Ceil(Min, coef);
if (Max % coef == 0) ans.second = Max / coef;
else ans.second = Round(Max, coef);
return ans;
}
int main() {
read(n), read(l);
pair <ll, ll> limx, limy;
limx = limy = make_pair(-l, l);
vis[0] = true;
for (int i = 1; i <= n; i++) {
read(t[i]), read(x[i]), read(y[i]);
ll tx = x[i] + y[i], ty = x[i] - y[i];
x[i] = tx, y[i] = ty;
if (abs(x[i]) % 2 != t[i] % 2 || abs(y[i]) % 2 != t[i] % 2) {
puts("NO");
return 0;
}
ll Mod = t[i] % l, Div = t[i] / l;
if (vis[Mod]) {
chkmax(limx.first, solve(px[Mod], make_pair(x[i], Div)));
chkmin(limx.second, solve(px[Mod], make_pair(x[i], Div)));
chkmax(limy.first, solve(py[Mod], make_pair(y[i], Div)));
chkmin(limy.second, solve(py[Mod], make_pair(y[i], Div)));
} else {
vis[Mod] = true;
px[Mod] = make_pair(x[i], Div);
py[Mod] = make_pair(y[i], Div);
}
}
vis[l] = true, px[l] = py[l] = make_pair(0, -1);
for (int i = 1, last = 0; i <= l; i++)
if (vis[i]) {
pair <ll, ll> tmp;
tmp = calc(px[last], px[i], i - last);
chkmax(limx.first, tmp.first);
chkmin(limx.second, tmp.second);
tmp = calc(py[last], py[i], i - last);
chkmax(limy.first, tmp.first);
chkmin(limy.second, tmp.second);
last = i;
}
if (abs(limx.first) % 2 != l % 2) limx.first++;
if (abs(limy.first) % 2 != l % 2) limy.first++;
if (limx.first > limx.second || limy.first > limy.second) {
puts("NO");
return 0;
}
ll sumx = limx.first, sumy = limy.first;
static bool ansx[MAXL], ansy[MAXL];
for (int i = 1, last = 0; i <= l; i++)
if (vis[i]) {
ll now, goal;
now = px[last].first - px[last].second * sumx;
goal = px[i].first - px[i].second * sumx;
for (int j = last + 1; j <= i; j++)
if (now <= goal) {
ansx[j] = true;
now++;
} else now--;
assert(now == goal);
now = py[last].first - py[last].second * sumy;
goal = py[i].first - py[i].second * sumy;
for (int j = last + 1; j <= i; j++)
if (now <= goal) {
ansy[j] = true;
now++;
} else now--;
assert(now == goal);
last = i;
}
for (int i = 1; i <= l; i++) {
if (ansx[i]) {
if (ansy[i]) putchar('R');
else putchar('U');
} else {
if (ansy[i]) putchar('D');
else putchar('L');
}
}
puts("");
return 0;
}
Codeforces 538H Summer Dichotomy
首先,若给定的图不是二分图,则显然无解。
考虑图是二分图的情况,每个联通块可以用其两侧点对应区间的交来描述。
考虑一个划分方案,使得 集合内区间的并为 , 集合内区间的并为 ,并且存在一个合法方案。那么,一定存在另一个合法方案使得 是 中的一者,且 是 中的一者,或者 是 $r_1,r_2 $ 中的一者,且 是 中的一者。
枚举这两种情况,以第一种为例。
对所有区间按照左端点排序,并枚举 ,考虑如何计算 。
左端点在 右侧的区间必须分在 集合内,因此不能包含同一连通块对应的所有区间。令 表示这些区间的右端点最小值, 表示这些区间的左端点最大值。
左端点在 左侧或相同的区间必须覆盖 ,若同一连通块对应的所有区间左端点均在 左侧或与 相同,应取右端点较大者划入 集合,令 这些区间的右端点最小值。
若存在合法的 , 应当取 的最小值,且应 。
实现可以参考以下代码,时间复杂度 。
在 Codeforces 的讨论版中存在一种较为简便的做法:
不考虑 的限制,则显然应取 ,判断 是否合法即可。
若
,则令
;
若
,则令
,然后判断
是否合法即可。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF = 1e9 + 7;
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 n, m, tot, ans[MAXN];
bool vis[MAXN], col[MAXN];
vector <int> e[MAXN], points[MAXN];
pair <int, int> s, tmp[2], v[MAXN], a[MAXN][2];
void foundAns(int x, int y) {
assert(s.first <= x + y && x + y <= s.second);
puts("POSSIBLE");
printf("%d %d\n", x, y);
static int ans[MAXN];
for (int i = 1; i <= tot; i++) {
if (a[i][0].first <= x && x <= a[i][0].second && a[i][1].first <= y && y <= a[i][1].second) {
for (auto x : points[i])
ans[x] = col[x];
} else if (a[i][0].first <= y && y <= a[i][0].second && a[i][1].first <= x && x <= a[i][1].second) {
for (auto x : points[i])
ans[x] = !col[x];
} else assert(false);
}
for (int i = 1; i <= n; i++)
printf("%d", ans[i] + 1);
printf("\n");
exit(0);
}
void solveMin() {
static pair <pair <int, int>, int> b[MAXN];
for (int i = 1, j = 0; i <= n; i++) {
b[++j] = make_pair(a[i][0], i);
b[++j] = make_pair(a[i][1], i);
}
sort(b + 1, b + 2 * tot + 1);
static int pre[MAXN], suf[MAXN];
pre[0] = suf[2 * tot + 1] = INF;
for (int i = 2 * tot; i >= 1; i--)
suf[i] = min(suf[i + 1], b[i].first.second);
for (int i = 1; i <= 2 * tot; i++)
pre[i] = min(pre[i - 1], b[i].first.second);
static int cnt[MAXN];
priority_queue <int, vector <int>, greater <int>> Heap, Delt;
for (int i = 1; i <= tot; i++)
Heap.push(max(a[i][0].second, a[i][1].second));
for (int i = 2 * tot, nxt; i >= 1; i = nxt) {
nxt = i; while (nxt >= 1 && b[nxt].first.first == b[i].first.first) nxt--;
while (!Heap.empty() && !Delt.empty() && Heap.top() == Delt.top()) {
Heap.pop();
Delt.pop();
}
int x = b[i].first.first, y = s.second - x;
if (!Heap.empty()) chkmin(y, Heap.top());
chkmin(y, suf[i + 1]);
if (b[i].first.first <= pre[i] && y >= b[2 * tot].first.first && x + y >= s.first) foundAns(x, y);
for (int j = nxt + 1; j <= i; j++) {
if (++cnt[b[j].second] == 2) return;
else Delt.push(max(a[b[j].second][0].second, a[b[j].second][1].second));
}
}
}
bool cmp(pair <pair <int, int>, int> x, pair <pair <int, int>, int> y) {
return x.first.second > y.first.second;
}
void solveMax() {
static pair <pair <int, int>, int> b[MAXN];
for (int i = 1, j = 0; i <= n; i++) {
b[++j] = make_pair(a[i][0], i);
b[++j] = make_pair(a[i][1], i);
}
sort(b + 1, b + 2 * tot + 1, cmp);
static int pre[MAXN], suf[MAXN];
pre[0] = suf[2 * tot + 1] = 0;
for (int i = 2 * tot; i >= 1; i--)
suf[i] = max(suf[i + 1], b[i].first.first);
for (int i = 1; i <= 2 * tot; i++)
pre[i] = max(pre[i - 1], b[i].first.first);
static int cnt[MAXN];
priority_queue <int> Heap, Delt;
for (int i = 1; i <= tot; i++)
Heap.push(min(a[i][0].first, a[i][1].first));
for (int i = 2 * tot, nxt; i >= 1; i = nxt) {
nxt = i; while (nxt >= 1 && b[nxt].first.second == b[i].first.second) nxt--;
while (!Heap.empty() && !Delt.empty() && Heap.top() == Delt.top()) {
Heap.pop();
Delt.pop();
}
int x = b[i].first.second, y = s.first - x;
if (!Heap.empty()) chkmax(y, Heap.top());
chkmax(y, suf[i + 1]);
if (b[i].first.second >= pre[i] && y <= b[2 * tot].first.second && x + y <= s.second) foundAns(x, y);
for (int j = nxt + 1; j <= i; j++) {
if (++cnt[b[j].second] == 2) return;
else Delt.push(min(a[b[j].second][0].first, a[b[j].second][1].first));
}
}
}
void dfs(int pos) {
vis[pos] = true;
points[tot].push_back(pos);
chkmax(tmp[col[pos]].first, v[pos].first);
chkmin(tmp[col[pos]].second, v[pos].second);
for (auto x : e[pos])
if (!vis[x]) {
col[x] = !col[pos];
dfs(x);
} else if (col[pos] == col[x]) {
puts("IMPOSSIBLE");
exit(0);
}
}
int main() {
read(s.first), read(s.second), read(n), read(m);
for (int i = 1; i <= n; i++)
read(v[i].first), read(v[i].second);
for (int i = 1; i <= m; i++) {
int x, y; read(x), read(y);
e[x].push_back(y);
e[y].push_back(x);
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
tmp[0] = tmp[1] = make_pair(0, INF);
tot++, dfs(i);
if (tmp[0].first > tmp[0].second || tmp[1].first > tmp[1].second) {
puts("IMPOSSIBLE");
return 0;
}
a[tot][0] = tmp[0];
a[tot][1] = tmp[1];
}
}
solveMin();
solveMax();
puts("IMPOSSIBLE");
return 0;
}
Codeforces 547D Mike and Fish
首先,若对于解的存在性不加证明,则显然可以用有上下界的网络流解题,时间复杂度 。
考虑解的存在性,首先假设图中每行每列均有偶数个点,那么,在这张二分图上每个联通块求欧拉回路即可构造出一组可行的解。
对于不满足每行每列均有偶数个点的情况,在度为奇数的点间两两连边,转化为以上情况即可。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5 + 5;
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 <pair <int, int>> a[MAXN];
int n, m, x[MAXN], y[MAXN]; unsigned cur[MAXN];
set <pair <int, int>> path;
bool vis[MAXN];
void work(int pos, int fa) {
for (unsigned &i = cur[pos]; i < a[pos].size(); i++)
if (!vis[a[pos][i].second]) {
vis[a[pos][i].second] = true;
work(a[pos][i].first, pos);
}
if (fa) path.insert(make_pair(fa, pos));
}
int main() {
n = 2e5, read(m);
for (int i = 1; i <= m; i++) {
read(x[i]), read(y[i]), y[i] += n;
a[x[i]].emplace_back(y[i], i);
a[y[i]].emplace_back(x[i], i);
}
vector <int> odd;
for (int i = 1; i <= 2 * n; i++)
if (a[i].size() & 1) odd.push_back(i);
int tot = m;
while (!odd.empty()) {
int x = odd.back(); odd.pop_back();
int y = odd.back(); odd.pop_back();
a[x].emplace_back(y, ++tot);
a[y].emplace_back(x, tot);
}
for (int i = 1; i <= 2 * n; i++)
work(i, 0);
for (int i = 1; i <= m; i++)
if (path.count(make_pair(x[i], y[i]))) putchar('b');
else putchar('r');
putchar('\n');
return 0;
}
Codeforces 547E Mike and Friends
离线询问,在后缀树上线段树合并即可。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5 + 5;
const int MAXQ = 5e5 + 5;
const int MAXP = 8e6 + 5;
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;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct SegmentTreeMerging {
struct Node {
int lc, rc, sum;
bool leaf;
} a[MAXP];
int size, n;
void init(int x) {
n = x;
size = 0;
}
void update(int root) {
a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum;
}
void modify(int &root, int l, int r, int pos, int d) {
if (root == 0) root = ++size;
if (l == r) {
a[root].sum += d;
a[root].leaf = true;
return;
}
int mid = (l + r) / 2;
if (mid >= pos) modify(a[root].lc, l, mid, pos, d);
else modify(a[root].rc, mid + 1, r, pos, d);
update(root);
}
void modify(int &root, int val, int d) {
modify(root, 1, n, val, d);
}
int merge(int x, int y) {
if (x == 0 || y == 0) return x + y;
if (a[x].leaf) {
a[x].sum += a[y].sum;
return x;
}
a[x].lc = merge(a[x].lc, a[y].lc);
a[x].rc = merge(a[x].rc, a[y].rc);
update(x);
return x;
}
void join(int &to, int from) {
to = merge(to, from);
}
int query(int root, int l, int r, int ql, int qr) {
if (root == 0) return 0;
if (l == ql && r == qr) return a[root].sum;
int mid = (l + r) / 2, ans = 0;
if (mid >= ql) ans += query(a[root].lc, l, mid, ql, min(mid, qr));
if (mid + 1 <= qr) ans += query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
return ans;
}
int query(int root, int l, int r) {
return query(root, 1, n, l, r);
}
} ST;
struct SuffixAutomaton {
struct Node {
int child[26];
int father, depth;
} a[MAXN];
vector <int> e[MAXN];
vector <int> home[MAXN];
vector <pair <int, int>> s[MAXN];
int n, root, size, last;
int newnode(int dep) {
a[++size].depth = dep;
return size;
}
void init() {
size = 0;
root = last = 0;
}
void extend(int ch) {
int p = last, np;
if (a[p].child[ch]) {
int q = a[p].child[ch];
if (a[p].depth + 1 == a[q].depth) np = q;
else {
np = newnode(a[p].depth + 1);
memcpy(a[np].child, a[q].child, sizeof(a[q].child));
a[np].father = a[q].father;
a[q].father = np;
while (a[p].child[ch] == q) {
a[p].child[ch] = np;
p = a[p].father;
}
}
} else {
np = newnode(a[p].depth + 1);
while (a[p].child[ch] == 0) {
a[p].child[ch] = np;
p = a[p].father;
}
if (a[p].child[ch] == np) a[np].father = root;
else {
int q = a[p].child[ch];
if (a[q].depth == a[p].depth + 1) a[np].father = q;
else {
int nq = newnode(a[p].depth + 1);
memcpy(a[nq].child, a[q].child, sizeof(a[q].child));
a[nq].father = a[q].father;
a[q].father = a[np].father = nq;
while (a[p].child[ch] == q) {
a[p].child[ch] = nq;
p = a[p].father;
}
}
}
}
last = np;
}
void insert(char *s) {
last = 0, n++;
int len = strlen(s + 1);
for (int i = 1; i <= len; i++) {
extend(s[i] - 'a');
home[n].push_back(last);
}
}
int rt[MAXN], ans[MAXQ];
vector <pair <pair <int, int>, int>> q[MAXN];
void work(int pos) {
for (auto x : s[pos])
ST.modify(rt[pos], x.first, x.second);
for (auto x : e[pos]) {
work(x);
ST.join(rt[pos], rt[x]);
}
for (auto x : q[pos])
ans[x.second] = ST.query(rt[pos], x.first.first, x.first.second);
}
void solve(int m) {
for (int i = 1; i <= size; i++)
e[a[i].father].push_back(i);
for (int i = 1; i <= n; i++)
for (unsigned j = 0; j < home[i].size(); j++)
s[home[i][j]].emplace_back(i, 1);
work(0);
for (int i = 1; i <= m; i++)
writeln(ans[i]);
}
} SAM;
char s[MAXN];
int main() {
int n, m; read(n), read(m);
SAM.init(), ST.init(n);
for (int i = 1; i <= n; i++) {
scanf("\n%s", s + 1);
SAM.insert(s);
}
for (int i = 1; i <= m; i++) {
int l, r, k; read(l), read(r), read(k);
SAM.q[SAM.home[k].back()].emplace_back(make_pair(l, r), i);
}
SAM.solve(m);
return 0;
}
Codeforces 549E Sasha Circle
建议参考 官方题解 阅读。
令 为圆内的点集, 为圆外的点集。
可以认为,当存在一个圆,使得 中的点都在圆内或圆上, 中的点都在圆外时,题目有解
若 中只有一个点,显然有解;若 ,容易看出如果题目有解,那么肯定存在一个解使得 中至少有两个点在圆上。
考虑枚举 中的两个点,那么圆心一定在两点连线的中垂线上。对于其他 中的点和 中的点,每个点必须在圆内或者必须在圆外,那么会得到一个圆心坐标的可行区间。
由此,可以得到一个时间复杂度为 的算法。
考虑转化问题。
将原平面作为 平面,将问题放到三维坐标系中讨论,画出抛物面 。
考虑抛物面和一个不与 垂直的平面相交得到的截面在 上的投影。
假设平面为 ,那么 ,
即 。
通过方程可以看出投影一定是一个圆。
那么考虑将 上的点 映射到 ,这是一一对应的。
也就是说 上的一个圆对应一个抛物面的截面,容易发现圆内的点一定在对应平面下方,圆外的点一定在对应平面上方,圆上的点一定在对应平面上。
于是问题转化为求一个平面,将抛物面上两点集分开,使得一点集在平面上方,另一点集在平面下方或平面上。
类比于平面上的情况,可以知道如果有解,则一定存在一个答案平面经过下方点集的上凸壳上两点,容易证明这两点一定在上凸壳上相邻,即对应上凸壳的一条边。
然后考虑上凸壳的边在 上的正投影,容易发现投影会是平面上对应凸包的一个剖分,当不存在四点共圆时,形成三角剖分,否则将其补成三角剖分。
由之前的讨论可知,需要枚举的点对就只有原凸包上的相邻点和剖分中的边的两端点。
考虑这个剖分的特征,回到凸壳上考虑,对于凸壳的每一个面,凸壳必然在其所在平面下面,对应到平面上就是凸包必然在三角剖分后每个三角形的外接圆内部。
所以只需要找到一个这样的剖分,需要枚举的边数就降到凸包的点数级别了。
考虑求出一个这样的剖分,考虑分治,在凸包上按逆(顺)时针讨论, 表示将点 到点 剖分。
将 这条边作为底边,找到一个点 ,使得这三个点构成三角形的外接圆最大,可以证明这个三角形会包含整个凸包,然后递归做 就好了。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const double eps = 1e-8;
const double INF = 1e99;
const double pi = acos(-1);
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;
}
struct point {double x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, int b) {return (point) {a.x * b, a.y * b}; }
double operator * (point a, point b) {return a.x * b.y - a.y * b.x; }
double dot(point a, point b) {return a.x * b.x + a.y * b.y; }
double dist(point a, point b) {
a = a - b;
return sqrt(a.x * a.x + a.y * a.y);
}
bool onseg(point a, point b, point c) {
return fabs(dist(a, b) - dist(a, c) - dist(b, c)) <= eps;
}
int n, m, top;
point p, a[MAXN], b[MAXN], c[MAXN];
vector <pair <int, int>> e;
bool cmp(point a, point b) {
if ((a - p) * (b - p) == 0) return dist(a, p) < dist(b, p);
else return (a - p) * (b - p) > 0;
}
void convexHull() {
for (int i = 1; i <= n; i++)
if (a[i].y < a[1].y || (a[i].y == a[1].y && a[i].x < a[1].x)) swap(a[i], a[1]);
p = c[top = 1] = a[1];
sort(a + 2, a + n + 1, cmp), a[n + 1] = p;
for (int i = 2; i <= n + 1; i++) {
while (top >= 2 + (i == n + 1) && (a[i] - c[top - 1]) * (c[top] - c[top - 1]) >= 0) top--;
c[++top] = a[i];
}
}
void work(int l, int r) {
e.emplace_back(l, r);
if (l == r - 1) return;
double Max = -INF; int pos = 0;
for (int i = l + 1; i <= r - 1; i++) {
double ang = dot(c[r] - c[i], c[l] - c[i]) / ((c[r] - c[i]) * (c[l] - c[i]));
if (ang > Max) {
Max = ang;
pos = i;
}
}
work(l, pos);
work(pos, r);
}
bool check(point l, point r) {
pair <double, double> rng = make_pair(-INF, INF);
for (int i = 1; i <= n; i++) {
if (fabs((a[i] - l) * (r - l)) <= eps) {
assert(onseg(l, r, a[i]));
continue;
}
if ((a[i] - l) * (r - l) > 0) {
double ang = dot(r - a[i], l - a[i]) / ((r - a[i]) * (l - a[i]));
chkmax(rng.first, ang);
} else {
double ang = dot(l - a[i], r - a[i]) / ((l - a[i]) * (r - a[i]));
chkmin(rng.second, -ang);
}
}
for (int i = 1; i <= m; i++) {
if (fabs((b[i] - l) * (r - l)) <= eps) {
if (onseg(l, r, b[i])) return false;
continue;
}
if ((b[i] - l) * (r - l) > 0) {
double ang = dot(r - b[i], l - b[i]) / ((r - b[i]) * (l - b[i]));
chkmin(rng.second, ang);
} else {
double ang = dot(l - b[i], r - b[i]) / ((l - b[i]) * (r - b[i]));
chkmax(rng.first, -ang);
}
}
return rng.first < rng.second - eps;
}
bool solve() {
if (n == 1) return true;
convexHull();
e.clear(), work(1, top - 1);
for (auto x : e)
if (check(c[x.first], c[x.second])) return true;
return false;
}
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++)
read(a[i].x), read(a[i].y);
for (int i = 1; i <= m; i++)
read(b[i].x), read(b[i].y);
if (solve()) {
puts("YES");
return 0;
}
swap(n, m);
swap(a, b);
if (solve()) {
puts("YES");
return 0;
}
puts("NO");
return 0;
}
Codeforces 553E Kyoya and Train
考虑一个暴力 dp ,记 表示在点 处,时刻 最优决策的期望花费。
则有
我们希望求出 。
用分治 FFT 优化该 dp 即可。
具体来说,我们可以轻松地得到 ,也可以快速地计算出 对后续 dp 的影响,即 。
那么考虑分治,对于时刻区间 ,取 ,首先计算出时刻区间 的 dp 值,然后用 FFT 计算时刻区间 的 dp 值对时刻区间 的影响,即 ,最后递归计算时刻区间 的 dp 值。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
const int MAXM = 32768;
const double INF = 1e18;
const double pi = acos(-1);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct point {double x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, point b) {return (point) {a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; }
point operator / (point a, double x) {return (point) {a.x / x, a.y / x}; }
int n, m, t, fine;
vector <pair <int, int> > e;
double dist[MAXN][MAXN], edge[MAXN][MAXN];
vector <double> dp[MAXN], trans[MAXN][MAXN], p[MAXN][MAXN];
int N, Log, home[MAXM]; point a[MAXM], b[MAXM], c[MAXM];
void FFT(point *a, int mode) {
for (int i = 0; i < N; i++)
if (home[i] > i) swap(a[i], a[home[i]]);
for (int len = 2; len <= N; len <<= 1) {
point delta = (point) {cos(2 * pi / len * mode), sin(2 * pi / len * mode)};
for (int i = 0; i < N; i += len) {
point now = (point) {1, 0};
for (int j = i, k = i + len / 2; k < i + len; j++, k++) {
point tmp = a[j], tnp = a[k];
a[j] = tmp + tnp * now;
a[k] = tmp - tnp * now;
now = now * delta;
}
}
}
if (mode == -1) {
for (int i = 0; i < N; i++)
a[i] = a[i] / N;
}
}
void transfer(int l, int mid, int r, vector <double> &dp, vector <double> &p, vector <double> &res) {
N = 1, Log = 0;
while (N <= (r - l) + (r - mid)) {
N <<= 1;
Log++;
}
for (int i = 0; i < N; i++) {
int tmp = i, res = 0;
for (int j = 1; j <= Log; j++) {
res <<= 1;
res += tmp & 1;
tmp >>= 1;
}
home[i] = res;
}
for (int i = 0; i < N; i++)
a[i] = b[i] = (point) {0, 0};
for (int i = mid + 1; i <= r; i++)
a[r - i].x = dp[i];
for (int i = 0; i <= r - l; i++)
b[i].x = p[i];
FFT(a, 1), FFT(b, 1);
for (int i = 0; i < N; i++)
c[i] = a[i] * b[i];
FFT(c, -1);
for (int i = l; i <= mid; i++)
res[i] += c[r - i].x;
}
void work(int l, int r) {
if (l == r) {
for (int i = 1; i <= n - 1; i++)
dp[i][l] = INF;
for (auto x : e) {
int i = x.first, j = x.second;
chkmin(dp[i][l], trans[i][j][l] + edge[i][j]);
}
return;
}
int mid = (l + r) / 2;
work(mid + 1, r);
for (auto x : e) {
int i = x.first, j = x.second;
transfer(l, mid, r, dp[j], p[i][j], trans[i][j]);
}
work(l, mid);
}
int main() {
read(n), read(m), read(t), read(fine);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (i == j) dist[i][j] = 0;
else dist[i][j] = INF;
for (int i = 1; i <= n; i++)
dp[i].resize(t + 1);
for (int i = 1; i <= m; i++) {
int x, y, z;
read(x), read(y), read(z);
if (dist[x][y] == INF) {
e.emplace_back(x, y);
edge[x][y] = z;
}
chkmin(dist[x][y], z * 1.0);
chkmin(edge[x][y], z * 1.0);
p[x][y].push_back(0);
trans[x][y].resize(t + 1);
for (int j = 1; j <= t; j++) {
int val; read(val);
p[x][y].push_back(val * 0.00001);
}
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
chkmin(dist[i][j], dist[i][k] + dist[k][j]);
for (auto x : e) {
int i = x.first, j = x.second;
double res = 1;
for (int k = t; k >= 0; k--) {
res -= p[i][j][t - k];
trans[i][j][k] = res * (dist[j][n] + fine);
}
}
work(0, t);
printf("%.10lf\n", dp[1][0]);
return 0;
}
Codeforces 555E Case of Computer Network
首先,可以将树上的边双连通分量缩成点,因为显然可以定向使得边双连通分量强连通。
此后,问题变成了森林上的问题,树上差分解决即可。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 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 <pair <int, int>> a[MAXN];
int n, m, q, top, tot, Stack[MAXN], belong[MAXN];
int timer, x[MAXN], y[MAXN], dfn[MAXN], low[MAXN], col[MAXN];
void work(int pos, int fa, int c) {
Stack[++top] = pos, col[pos] = c;
dfn[pos] = low[pos] = ++timer;
for (auto x : a[pos])
if (dfn[x.first] == 0) {
work(x.first, x.second, c);
chkmin(low[pos], low[x.first]);
} else if (x.second != fa) chkmin(low[pos], dfn[x.first]);
if (dfn[pos] == low[pos]) {
int tmp = Stack[top--];
belong[tmp] = ++tot;
while (tmp != pos) {
tmp = Stack[top--];
belong[tmp] = tot;
}
}
}
vector <int> b[MAXN];
int depth[MAXN], father[MAXN][MAXLOG];
void dfs(int pos, int fa) {
father[pos][0] = fa;
depth[pos] = depth[fa] + 1;
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) dfs(b[pos][i], pos);
}
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];
}
bool vis[MAXN];
int sum[MAXN][2];
void getans(int pos, int fa) {
vis[pos] = true;
for (auto x : b[pos])
if (x != fa) {
getans(x, pos);
sum[pos][0] += sum[x][0];
sum[pos][1] += sum[x][1];
}
if (sum[pos][0] && sum[pos][1]) {
puts("No");
exit(0);
}
}
int main() {
read(n), read(m), read(q);
for (int i = 1; i <= m; i++) {
read(x[i]), read(y[i]);
a[x[i]].emplace_back(y[i], i);
a[y[i]].emplace_back(x[i], i);
}
for (int i = 1, j = 0; i <= n; i++)
if (dfn[i] == 0) work(i, 0, ++j);
for (int i = 1; i <= m; i++)
if (belong[x[i]] != belong[y[i]]) {
b[belong[x[i]]].push_back(belong[y[i]]);
b[belong[y[i]]].push_back(belong[x[i]]);
}
for (int i = 1; i <= tot; i++)
if (depth[i] == 0) dfs(i, 0);
for (int i = 1; i <= q; i++) {
int x, y; read(x), read(y);
if (col[x] != col[y]) {
puts("No");
return 0;
}
x = belong[x], y = belong[y];
int z = lca(x, y);
sum[x][0]++, sum[z][0]--;
sum[y][1]++, sum[z][1]--;
}
for (int i = 1; i <= tot; i++)
if (!vis[i]) getans(i, 0);
puts("Yes");
return 0;
}
Codeforces 559E Gerald and Path
考虑按照从左向右的顺序 DP 。
由于可能的分界点数是 的,可以记 表示 在第 个分界点 左侧的灯塔照到的最右侧的位置是第 个分界点时,最优的覆盖长度。
转移时考虑 枚举其右侧的下一个被完全覆盖的区间 ,判断其是否能够被完全覆盖,若可以,则将 转移至 。
那么,剩余的问题就在于判断区间 是否能够被完全覆盖。
枚举枚举左起第一盏照到 的灯,其右侧的灯都应照向右侧,由此,可以将其递归为一个子问题。
以下代码的时间复杂度为 ,可简单优化至 ,但实际运行迅速。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
const int INF = 5e8;
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;
}
pair <int, int> a[MAXN];
unordered_map <ll, bool> mp;
int n, dp[MAXN][2], c[MAXN][2][MAXN][2];
bool solve(int l, int r) {
if (l >= r) return true;
ll tmp = 1ll * INF * l + r;
if (mp.count(tmp)) return mp[tmp];
bool &ans = mp[tmp];
ans = false; int Max = l;
for (int i = 1; i <= n && !ans; i++)
if (a[i].first > l && a[i].first < r) {
if (a[i].first - a[i].second <= l) {
int tmp = max(Max, a[i].first);
for (int j = i + 1; j <= n && a[j].first <= tmp; j++)
chkmax(tmp, a[j].first + a[j].second);
ans |= solve(tmp, r);
}
chkmax(Max, a[i].first + a[i].second);
}
return ans;
}
int coef(int l, int tl, int r, int tr) {
if (c[l][tl][r][tr] != -INF - 1) return c[l][tl][r][tr];
int &ans = c[l][tl][r][tr]; ans = -INF;
int bl = a[l].first - (tl == 0) * a[l].second, cl = a[l].first + (tl == 1) * a[l].second;
int br = a[r].first - (tr == 0) * a[r].second, cr = a[r].first + (tr == 1) * a[r].second;
if (bl > br || cl > cr || (l == r && tl != tr)) return ans;
for (int i = 1; i <= n; i++)
if (a[i].first >= bl && a[i].first <= cl && i != l) chkmax(cl, a[i].first + a[i].second);
for (int i = n; i >= 1; i--)
if (a[i].first <= cr && a[i].first >= br && i != r) chkmin(br, a[i].first - a[i].second);
if (solve(cl, br)) return ans = cr - bl;
return ans;
}
int main() {
read(n);
a[0] = make_pair(-INF, 0);
for (int i = 1; i <= n; i++)
read(a[i].first), read(a[i].second);
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
c[i][0][j][0] = c[i][0][j][1] = c[i][1][j][0] = c[i][1][j][1] = -INF - 1;
for (int i = 0; i <= n; i++) {
int tmp = dp[i][0];
for (int j = i + 1; j <= n; j++)
for (int k = i + 1; k <= n; k++) {
if (a[j].first > a[i].first) chkmax(dp[k][0], tmp + coef(j, 1, k, 0));
if (a[j].first > a[i].first) chkmax(dp[k][1], tmp + coef(j, 1, k, 1));
if (a[j].first - a[j].second > a[i].first) chkmax(dp[k][0], tmp + coef(j, 0, k, 0));
if (a[j].first - a[j].second > a[i].first) chkmax(dp[k][1], tmp + coef(j, 0, k, 1));
}
tmp = dp[i][1];
for (int j = i + 1; j <= n; j++)
for (int k = i + 1; k <= n; k++) {
if (a[j].first > a[i].first + a[i].second) chkmax(dp[k][0], tmp + coef(j, 1, k, 0));
if (a[j].first > a[i].first + a[i].second) chkmax(dp[k][1], tmp + coef(j, 1, k, 1));
if (a[j].first - a[j].second > a[i].first + a[i].second) chkmax(dp[k][0], tmp + coef(j, 0, k, 0));
if (a[j].first - a[j].second > a[i].first + a[i].second) chkmax(dp[k][1], tmp + coef(j, 0, k, 1));
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
chkmax(ans, dp[i][0]);
chkmax(ans, dp[i][1]);
}
cout << ans << endl;
return 0;
}