P4515 [COCI2009-2010#6] XOR
一看範圍那麼小,就知道是狀壓暴力容斥。題目可以轉化為,求 \(t\) 個三角形的交的面積,乘一個係數 \(k_t\) 作為貢獻。
首先第一步,求交的面積。由於三角形都是相同方向的,直角邊平行座標軸的等腰直角三角形,我們有除了計算幾何以外更方便的方法。
考慮到如果有交,那麼交一定也是一個三角形。這個三角形的左下角頂點座標很好求,是 \((\max(x_i),\max(y_i))\)。那麼就差求邊長或者斜邊了。顯然,斜邊一定是最靠“左下”的那根直線,即 \(x + y\) 最小的那根直線,這個其實就是直線 \(x+y=\min(x_i+y_i+r_i)\)。這樣,左下角頂點和斜邊方程找到以後,面積也就好求很多了。\(S=\dfrac{(\min(x_i+y_i+r_i)-\max(x_i)-\max(y_i))^2}{2}\)
然後第二步,求容斥係數。我們需要係數 \(k_i\),滿足 \(t\) 個三角形的交被算了恰好 \([2 \nmid t]\) 次。即
\[\sum_{i=0}^t{t \choose i}k_i=[2 \nmid t] \]
看起來好像二項式反演的式子。我們設 \(g_t=[2 \nmid t]\),那麼:
\[g_t=\sum_{i=0}^t {t \choose i}k_i \]
\[k_t=\sum_{i=0}^t {t \choose i} (-1)^{t-i}g_i \]
\(i\) 為偶數的時候沒有貢獻,那麼:
\[k_t=(-1)^{t-1}\sum_{i=0}^t {t \choose i}[2 \nmid i] \]
二項式定理:
\[k_t=(-1)^{t-1}\frac{(1+1)^t-(1-1)^t}{2}=(-2)^{t-1} \]
最後直接暴力\(2^n\)容斥即可。
#include <cstdlib> #include <cstdio> #include <iostream> #include <cmath> #include <string> #include <cstring> #include <ctime> #include <algorithm> typedef long long ll; template <typename T> inline void read(T &x) { x = 0; char c = getchar(); bool flag = false; while (!isdigit(c)) { if (c == '-') flag = true; c = getchar(); } while (isdigit(c)) { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } if (flag) x = -x; } using namespace std; const int inf = 987654321; inline void MIN(int &a, int b) { if (b < a) a = b; } inline void MAX(int &a, int b) { if (b > a) a = b; } int n; int X[12], Y[12], R[12]; int mi[12]; ll ans; inline ll Pow(int x) { return 1ll * x * x; } inline void work(int s) { int mn = inf, mxx = -inf, mxy = -inf, ct = 0; for (register int i = 0; i < n; ++i) if ((s >> i) & 1) { MIN(mn, X[i] + Y[i] + R[i]); MAX(mxx, X[i]); MAX(mxy, Y[i]); ++ct; } ans += 1ll * mi[ct - 1] * Pow(max(0, mn - mxx - mxy)); } int main() { read(n); for (register int i = 0; i < n; ++i) read(X[i]), read(Y[i]), read(R[i]); mi[0] = 1; for (register int i = 1; i <= n; ++i) mi[i] = -2 * mi[i - 1]; int All = (1 << n) - 1; for (register int s = 1; s <= All; ++s) { work(s); } printf("%.1lf\n", 0.5 * ans); return 0; }