\(\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;
}
posted on 2021-08-06 15:13  Oxide  阅读(44)  评论(0编辑  收藏  举报