\(\text{bibi}\) 时间到!
这种题我能想出来就有鬼了。
题目
解法
有一个先导结论 ——
假设选取的三角形集合为 \(S\),那么 \(S\) 中三角形相交形成的三角形的面积为:
\[f(S)=\frac{1}{2}(\min\{x_i+y_i+r_i\}-\max x_i-\max y_i) \]
具体证明的话,懒得画图,就用文字叙述吧:
- \(x_i+y_i+r_i\) 能表示三角形 \(i\) 斜边形成直线的截距。
- 相交三角形的斜边截距最小,左下角横纵坐标均为最大。
枚举所选三角形集合进行容斥:
\[\text{Ans}=\sum_{\empty \subsetneq S\subseteq U}g(|S|)\cdot f(S)
\]
其中 \(g\) 是要算的容斥系数,它一般只与所选集合 大小 有关。
类似容斥原理的证明,我们设某个三角形被 \(m\) 个三角形包含,就有:
\[\sum_{i=1}^m \binom{m}{i}\cdot g(i)=[2\nmid m]
\]
左边那个很像二项式反演,于是我们令 \(h(m)=[2\nmid m]\)。就有:
\[h(m)=\sum_{i=1}^m \binom{m}{i}\cdot g(i)
\]
\[g(m)=\sum_{i=1}^m (-1)^{m-i} \binom{m}{i}h(i)
\]
\[g(m)=\sum_{i=1}^m (-1)^{m-i} \binom{m}{i}[2\nmid i]
\]
推到这里应该就可以了,我没有试过,可以预处理一下 \(g\)。
不过其实还可以化简。虽然 \([2\nmid i]\) 看上去很难搞,但有一个这样的式子:
\[(a+b)^n-(a-b)^n=\sum_{i=0}^n\binom{n}{i}a^{n-i}b^{i}-\sum_{i=0}^n(-1)^{i}\binom{n}{i}a^{n-i}b^{i}
\]
\[=2\sum_{i=0}^n [2\nmid i]\binom{n}{i}a^{n-i}b^i
\]
\((-1)^{m-i}\) 肿么办呢?由于只有 \([2\nmid i]\) 有贡献,所有的 \((-1)^{m-i}\) 都相当于 \((-1)^{m-1}\),而且还可以添一个 \(i=0\)。所以可以改写为:
\[g(m)=(-1)^{m-1}\sum_{i=0}^m [2\nmid i]\binom{m}{i}
\]
\[=(-1)^{m-1}\times \frac{(1+1)^m-(1-1)^m}{2}=(-2)^{m-1}
\]
然后直接枚举集合计算贡献。时间复杂度 \(\mathcal O(n\cdot2^n)\)。另外那个神仙三维差分还是算了吧。而且这题暴力数三角形完全可过好吧!
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' or s<'0')
f|=(s=='-');
while(s>='0' and s<='9')
x=(x<<1)+(x<<3)+(s^48),
s=getchar();
return f?-x:x;
}
template <class T>
inline void write(const T x) {
if(x<0) {
putchar('-'),write(-x);
return;
}
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <iostream>
using namespace std;
const int maxn=1300,inf=1e7;
int x[20],y[20],r[20],c[20];
int n,Pow[maxn];
int main() {
n=read(9);
for(int i=1;i<=n;++i)
x[i]=read(9),y[i]=read(9),
r[i]=read(9),c[i]=x[i]+y[i]+r[i];
Pow[0]=1;
for(int i=1;i<=n;++i)
Pow[i]=Pow[i-1]*(-2);
double ans=0;
for(int i=0;i<(1<<n);++i) {
int minc=inf,maxx=-inf,maxy=-inf,cnt=0;
for(int j=1;j<=n;++j)
if(i>>j-1&1)
++cnt,
minc=min(minc,c[j]),
maxx=max(maxx,x[j]),
maxy=max(maxy,y[j]);
if(minc-maxx-maxy>=0)
ans+=1.0*(minc-maxx-maxy)*(minc-maxx-maxy)/2*Pow[cnt-1];
}
printf("%.1f\n",ans);
return 0;
}