【hdu5448】Marisa’s Cake
题意:给你一个包含n个点的凸包,所有可能出现的子凸包的面积和。
分析:首先比较暴力的方法就是n^2枚举一条边,求它对答案的贡献。但是点的个数有10^5,因此我们考虑从某一个点出发的所有有向面积。
若我们当前考虑的是第i个点,那么它与第i+1个点形成的这条边所能产生的凸包个数为(2^(n - 2) - 1)个,与第i + 2个点形成的这条边的个数为(2 ^ (n - 3) - 1)个……又因为叉积满足分配律,因此可以先搞出来一个前缀和。而当在考虑第i + 1个点时,只需要把第i + 1个点的贡献减掉,然后再把除了第i个点的贡献乘2+1即可。复杂度为o(n)
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #define Rep(i, x, y) for (int i = x; i <= y; i ++) #define Dwn(i, x, y) for (int i = x; i >= y; i --) #define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex) using namespace std; typedef long long LL; const int mod = 1000000007, N = 100005; int T, n, ans; LL a[N], b[N], sa[N], sb[N], s2[N]; LL Cross(int i, int j) { return (a[i] * b[j] % mod - b[i] * a[j] % mod + mod) % mod; } int main() { scanf("%d", &T); while (T --) { scanf ("%d", &n); s2[0] = 1; Rep(i, 1, n) s2[i] = s2[i - 1] * 2 % mod; ans = 0, a[n + 1] = b[n + 1] = 0; Rep(i, 1, n) scanf ("%I64d%I64d", &a[i], &b[i]); Rep(i, 1, n) { sa[i] = (sa[i - 1] + a[i]) % mod; sb[i] = (sb[i - 1] + b[i]) % mod; } Rep(i, 2, n - 1) { (a[n + 1] += a[i] * (s2[n - i] - 1)) %= mod; (b[n + 1] += b[i] * (s2[n - i] - 1)) %= mod; } Rep(i, 1, n) { ans = (ans + Cross(i, n + 1)) % mod; int x = i - 1, y = i % n + 1; if (!x) x = n; (a[n + 1] -= a[y] * (s2[n - 2] - 1) % mod - mod) %= mod; (b[n + 1] -= b[y] * (s2[n - 2] - 1) % mod - mod) %= mod; a[n + 1] = ((a[n + 1]) * 2 + (sa[n] - a[y] - a[i]) % mod + mod) % mod; b[n + 1] = ((b[n + 1]) * 2 + (sb[n] - b[y] - b[i]) % mod + mod) % mod; } printf("%d\n", ans); } return 0; }