Search for a command to run...
Hints
Open only as much as you need to keep the solve alive.
Forget graphs for a second. For two quadratics, look at . They are independent exactly when this difference never becomes .
A nonzero quadratic has no real roots iff its discriminant is negative. If the difference becomes linear, it always has a real root, so equal needs special handling.
Define when for every real . Two functions are independent exactly when one is below the other in this sense.
This relation is transitive: if and , then . So an organized set is not some cursed arbitrary clique problem; it is just a chain in a DAG.
For each vertex , compute the longest chain ending at and the longest chain starting at . The answer is their sum minus because gets counted twice.
Research note: I checked the official Codeforces Round 1080 editorial/code, the discussion under it, and Arpa's video-tutorial post listing an accepted F submission. The official written tutorial body was not really present, but the posted code and comments point to the DAG/longest-chain approach. The statement here is still the source of truth.
For two functions and , independence means
Equivalently, the difference never equals . Write
where , , and .
We want to know when is strictly below everywhere, meaning for all . That is the same as for all real .
There are three cases:
Also, if , then as , so cannot be below everywhere.
So define a directed relation if for every real . Then two functions are independent iff exactly one of or holds.
The key structural observation is that this relation is transitive. If for all , and for all , then clearly for all . No magic, just inequality addition doing its job.
Therefore the functions form a strict partial order. An organized subset is a subset where every pair is independent, meaning every pair is comparable in this order. In a partial order, a pairwise comparable subset is a chain. So the task becomes:
For every vertex in this DAG, find the maximum size of a chain containing .
Build a DAG with an edge when , meaning is below everywhere. This DAG is acyclic because along an edge either strictly increases, or and are equal and strictly increases.
Now compute two DP arrays:
Using a topological order:
Then process the same topological order in reverse:
For a fixed function , any organized subset containing it splits into things below , then , then things above . The lower part plus has size at most , and plus the upper part has size at most . So the answer is at most
This bound is achievable: take an optimal chain ending at and an optimal chain starting at , glue them at , and transitivity guarantees every lower element is below every upper element. So the answer is exactly
Edge cases:
long long, but __int128 makes the discriminant check idiot-proof.Let be the number of edges. We test all ordered pairs in and then do DP in . Since , each test case is time and memory. The statement guarantees , so this is comfortably fine.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Quad {
ll a, b, c;
};
bool below(const Quad& f, const Quad& g) {
if (f.a == g.a) {
return f.b == g.b && f.c < g.c;
}
if (f.a > g.a) return false;
__int128 A = (__int128)g.a - f.a;
__int128 B = (__int128)g.b - f.b;
__int128 C = (__int128)g.c - f.c;
__int128 disc = B * B - 4 * A * C;
return disc < 0;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<Quad> q(n);
for (auto& f : q) cin >> f.a >> f.b >> f.c;
vector<vector<int>> adj(n);
vector<int> indeg(n, 0);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i != j && below(q[i], q[j])) {
adj[i].push_back(j);
indeg[j]++;
}
}
}
queue<int> que;
for (int i = 0; i < n; i++) {
if (indeg[i] == 0) que.push(i);
}
vector<int> topo;
while (!que.empty()) {
int u = que.front();
que.pop();
topo.push_back(u);
for (int v : adj[u]) {
indeg[v]--;
if (indeg[v] == 0) que.push(v);
}
}
vector<int> endAt(n, 1), startAt(n, 1);
for (int u : topo) {
for (int v : adj[u]) {
endAt[v] = max(endAt[v], endAt[u] + 1);
}
}
for (int i = n - 1; i >= 0; i--) {
int u = topo[i];
for (int v : adj[u]) {
startAt[u] = max(startAt[u], startAt[v] + 1);
}
}
for (int i = 0; i < n; i++) {
if (i) cout << ' ';
cout << endAt[i] + startAt[i] - 1;
}
cout << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Quad {
ll a, b, c;
};
bool below(const Quad& f, const Quad& g) {
if (f.a == g.a) {
return f.b == g.b && f.c < g.c;
}
if (f.a > g.a) return false;
__int128 A = (__int128)g.a - f.a;
__int128 B = (__int128)g.b - f.b;
__int128 C = (__int128)g.c - f.c;
__int128 disc = B * B - 4 * A * C;
return disc < 0;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<Quad> q(n);
for (auto& f : q) cin >> f.a >> f.b >> f.c;
vector<vector<int>> adj(n);
vector<int> indeg(n, 0);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i != j && below(q[i], q[j])) {
adj[i].push_back(j);
indeg[j]++;
}
}
}
queue<int> que;
for (int i = 0; i < n; i++) {
if (indeg[i] == 0) que.push(i);
}
vector<int> topo;
while (!que.empty()) {
int u = que.front();
que.pop();
topo.push_back(u);
for (int v : adj[u]) {
indeg[v]--;
if (indeg[v] == 0) que.push(v);
}
}
vector<int> endAt(n, 1), startAt(n, 1);
for (int u : topo) {
for (int v : adj[u]) {
endAt[v] = max(endAt[v], endAt[u] + 1);
}
}
for (int i = n - 1; i >= 0; i--) {
int u = topo[i];
for (int v : adj[u]) {
startAt[u] = max(startAt[u], startAt[v] + 1);
}
}
for (int i = 0; i < n; i++) {
if (i) cout << ' ';
cout << endAt[i] + startAt[i] - 1;
}
cout << '\n';
}
}