Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
First delete the geometry from your brain. Make a graph: two cities are adjacent iff their squared distance is exactly . For a fireworks set , every vertex outside contributes the number of adjacent vertices inside .
This distance graph is always bipartite on lattice points. The parity proof comes from removing the largest square power of from : every valid edge flips a suitable parity coloring. So one bipartition side has size at most .
Let the smaller side be and the other side be . Enumerate which vertices of launch fireworks. If this fixed set is , then every unlaunched vertex contributes the already-known value .
The annoying part is the unlaunched vertices of . For a fixed selected set in , the factor means: vertex either chooses no witness, or chooses one selected neighboring vertex in as its witness.
For each fixed , run a mask DP over . State means the vertices of that have already chosen a witness. Processing a vertex : if it is not selected, multiply by ; if it is selected, it may become the witness for any subset of , which is exactly a subset-zeta transition over those bits. Subtract at the end for the forbidden full set.
Graph Translation
Connect two cities by an edge if their squared distance is . Then the problem is purely graph-theoretic:
For every proper subset of vertices, add
Trying all subsets is obviously cooked. is small, but not that small.
Why The Graph Is Bipartite
The key geometric fact is that this graph is always bipartite.
Write where is odd, and let . For any edge vector , we have
Both and are divisible by . In one connected component, all coordinates are therefore fixed modulo , so we can divide coordinate differences from some root by .
Now look at the reduced edge vector .
If is even, then is odd, so exactly one of is odd. Thus the parity of flips across every edge.
If is odd, then , so both are odd. Thus the parity of flips across every edge.
Either way, every edge flips a parity color. No odd cycles. Bipartite. Nice little parity jumpscare.
Let the bipartition be , and choose as the smaller side. Then .
Fixing The Small Side
We enumerate , where is the set of vertices in that launch fireworks.
Let
Now we need to sum over all choices of fireworks launched from the other side.
Because the graph is bipartite, there are no edges inside or inside .
For a vertex :
For a vertex , its final contribution is
The product over all is the hard part.
The Witness Interpretation
For fixed , the value
counts choices where every does one of these:
So instead of multiplying counts at the end, we count these witness assignments directly while processing vertices of .
DP For One Fixed
Index the vertices of from to . We use a DP over masks of .
Let be the total weight after processing some vertices of , where exactly the vertices in mask have already chosen a witness.
Initially:
Now process one vertex .
Let
and let be the mask of neighbors of inside .
There are two cases.
If is not selected, then every state is multiplied by :
If is selected, then it can be chosen as witness by any subset of its neighboring vertices in that have not already chosen a witness. Equivalently, for a final mask , the previous mask could have been for any :
That sum is exactly a subset-zeta transform over only the bits in . Start with a copy of , and for each bit in , do the usual
for masks containing that bit.
After all vertices of are processed, we add all , because vertices of not in simply chose the no-witness option.
We do this for every .
The only invalid fireworks set counted is the full set of all vertices. Its product is the empty product , so subtract at the end.
Complexity
Let . For a fixed , the DP has size . The zeta transitions cost per processed vertex in .
Overall:
which is fine for and the given limits. Memory is .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
const int MOD = 998244353;
int tc;
cin >> tc;
while (tc--) {
int n, k;
cin >> n >> k;
vector<int> x(n), y(n);
for (int i = 0; i < n; i++) cin >> x[i] >> y[i];
vector<vector<int>> g(n);
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int dx = x[i] - x[j];
int dy = y[i] - y[j];
if (dx * dx + dy * dy == k) {
g[i].push_back(j);
g[j].push_back(i);
}
}
}
vector<int> color(n, -1);
for (int s = 0; s < n; s++) {
if (color[s] != -1) continue;
color[s] = 0;
queue<int> q;
q.push(s);
while (!q.empty()) {
int v = q.front();
q.pop();
for (int to : g[v]) {
if (color[to] == -1) {
color[to] = color[v] ^ 1;
q.push(to);
}
}
}
}
vector<int> part[2];
for (int i = 0; i < n; i++) part[color[i]].push_back(i);
if (part[0].size() > part[1].size()) swap(part[0], part[1]);
vector<int>& A = part[0];
vector<int>& B = part[1];
int K = (int)A.size();
vector<int> pos(n, -1);
for (int i = 0; i < K; i++) pos[A[i]] = i;
vector<int> bmask;
for (int b : B) {
int mask = 0;
for (int v : g[b]) {
if (pos[v] != -1) mask |= 1 << pos[v];
}
bmask.push_back(mask);
}
int maxState = 1 << K;
vector<int> dp(maxState), zeta(maxState);
ll ans = 0;
int allA = (1 << K) - 1;
for (int S = 0; S <= allA; S++) {
int comp[16];
int m = 0;
for (int i = 0; i < K; i++) {
if ((S >> i & 1) == 0) comp[i] = m++;
}
int sz = 1 << m;
fill(dp.begin(), dp.begin() + sz, 0);
dp[0] = 1;
int notS = allA ^ S;
for (int fullMask : bmask) {
int c = __builtin_popcount((unsigned)(fullMask & S)) + 1;
for (int mask = 0; mask < sz; mask++) zeta[mask] = dp[mask];
int oldNeighbors = fullMask & notS;
int neigh = 0;
for (int i = 0; i < K; i++) {
if (oldNeighbors >> i & 1) neigh |= 1 << comp[i];
}
for (int bitId = 0; bitId < m; bitId++) {
if ((neigh >> bitId & 1) == 0) continue;
int bit = 1 << bitId;
for (int base = 0; base < sz; base += bit << 1) {
for (int off = 0; off < bit; off++) {
int& val = zeta[base + bit + off];
val += zeta[base + off];
if (val >= MOD) val -= MOD;
}
}
}
for (int mask = 0; mask < sz; mask++) {
dp[mask] = ((ll)dp[mask] * c + zeta[mask]) % MOD;
}
}
for (int mask = 0; mask < sz; mask++) {
ans += dp[mask];
if (ans >= MOD) ans -= MOD;
}
}
ans = (ans - 1 + MOD) % MOD;
cout << ans << char(10);
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
const int MOD = 998244353;
int tc;
cin >> tc;
while (tc--) {
int n, k;
cin >> n >> k;
vector<int> x(n), y(n);
for (int i = 0; i < n; i++) cin >> x[i] >> y[i];
vector<vector<int>> g(n);
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int dx = x[i] - x[j];
int dy = y[i] - y[j];
if (dx * dx + dy * dy == k) {
g[i].push_back(j);
g[j].push_back(i);
}
}
}
vector<int> color(n, -1);
for (int s = 0; s < n; s++) {
if (color[s] != -1) continue;
color[s] = 0;
queue<int> q;
q.push(s);
while (!q.empty()) {
int v = q.front();
q.pop();
for (int to : g[v]) {
if (color[to] == -1) {
color[to] = color[v] ^ 1;
q.push(to);
}
}
}
}
vector<int> part[2];
for (int i = 0; i < n; i++) part[color[i]].push_back(i);
if (part[0].size() > part[1].size()) swap(part[0], part[1]);
vector<int>& A = part[0];
vector<int>& B = part[1];
int K = (int)A.size();
vector<int> pos(n, -1);
for (int i = 0; i < K; i++) pos[A[i]] = i;
vector<int> bmask;
for (int b : B) {
int mask = 0;
for (int v : g[b]) {
if (pos[v] != -1) mask |= 1 << pos[v];
}
bmask.push_back(mask);
}
int maxState = 1 << K;
vector<int> dp(maxState), zeta(maxState);
ll ans = 0;
int allA = (1 << K) - 1;
for (int S = 0; S <= allA; S++) {
int comp[16];
int m = 0;
for (int i = 0; i < K; i++) {
if ((S >> i & 1) == 0) comp[i] = m++;
}
int sz = 1 << m;
fill(dp.begin(), dp.begin() + sz, 0);
dp[0] = 1;
int notS = allA ^ S;
for (int fullMask : bmask) {
int c = __builtin_popcount((unsigned)(fullMask & S)) + 1;
for (int mask = 0; mask < sz; mask++) zeta[mask] = dp[mask];
int oldNeighbors = fullMask & notS;
int neigh = 0;
for (int i = 0; i < K; i++) {
if (oldNeighbors >> i & 1) neigh |= 1 << comp[i];
}
for (int bitId = 0; bitId < m; bitId++) {
if ((neigh >> bitId & 1) == 0) continue;
int bit = 1 << bitId;
for (int base = 0; base < sz; base += bit << 1) {
for (int off = 0; off < bit; off++) {
int& val = zeta[base + bit + off];
val += zeta[base + off];
if (val >= MOD) val -= MOD;
}
}
}
for (int mask = 0; mask < sz; mask++) {
dp[mask] = ((ll)dp[mask] * c + zeta[mask]) % MOD;
}
}
for (int mask = 0; mask < sz; mask++) {
ans += dp[mask];
if (ans >= MOD) ans -= MOD;
}
}
ans = (ans - 1 + MOD) % MOD;
cout << ans << char(10);
}
}