题目:http://acm.hdu.edu.cn/showproblem.php?pid=5448
题意:
给定一个由 n 个整点构成的凸多边形,求从 n 个点里任意选不少于 3 个点组成的凸多边形面积之和,显然整点构成的多边形面积一定是 0.5 的整数倍,题目需要你算出答案的 2 倍 mod1000000007 的值。
n≤100000 ,坐标 ∈[0,109] 。
题解:
面积乘 2 的话直接算叉积就好了,例如三个点 A,B,C 构成的三角形,设从原点到他们的向量为 fA,fB,fC ,那么三角形面积的 2 倍就是 fA×fB+fB×fC+fC×fA ,同理 k 个点(对应向量按逆时针排序为 f1,f2,...,fk )构成的多边形面积的 2 倍等于 ∑k−1i=1fi×fi+1+fk×f1 。
直接算每种情况的多边形求面积是 O(n2n) 的,但是我们可以发现计算面积的过程中有很多的重复计算。
对于两个在原多边形上的点 i,j ,我们可以算出它们产生的贡献 fi×fj 在多少个需要算的多边形 M={a0,a1,...,ap−1} 的式子里出现。
首先如果 at=i ,那么必须有 a(t+1)modp=j ,也就是说按逆时针顺序在 i 和 j 之间的点都不能选,而且为了保证 p≥3 ,所以按逆时针顺序在 j 到 i 这一段的点至少要有一个在多边形 M 上,设 j 到 i 之间有 c 个点,那么对应的方案数就是 2c−1 ,于是我们得到了任意两个点之间向量叉积的系数。
于是答案可以被表示为
代码:
#include <cstdio>
typedef long long LL;
const int maxn = 100010, mod = 1000000007, inv2 = 500000004;
int t, n, pw1[maxn], pw2[maxn], x, y, pre1x, pre1y, pre2x, pre2y, ans;
int mod_add(int x, int y)
{
x += y;
if(x >= mod)
x -= mod;
return x;
}
int mod_sub(int x, int y)
{
x -= y;
if(x < 0)
x += mod;
return x;
}
int det(int x1, int y1, int x2, int y2)
{
return mod_sub((LL)x1 * y2 % mod, (LL)x2 * y1 % mod);
}
int main()
{
scanf("%d", &t);
pw1[0] = 1;
for(int i = 1; i < maxn; ++i)
pw1[i] = (LL)pw1[i - 1] * 2 % mod; // +
pw2[0] = 1;
for(int i = 1; i < maxn; ++i)
pw2[i] = (LL)pw2[i - 1] * inv2 % mod; // -
while(t--)
{
scanf("%d", &n);
pre1x = pre1y = pre2x = pre2y = ans = 0;
for(int i = 1; i <= n; ++i)
{
scanf("%d%d", &x, &y);
ans = mod_add(ans, (LL)pw1[i - 1] * det(x, y, pre2x, pre2y) % mod);
ans = mod_add(ans, (LL)(i == n ? inv2 : pw1[n - i - 1]) * det(pre1x, pre1y, x, y) % mod);
pre1x = mod_add(pre1x, (LL)x * pw1[i] % mod); // +
pre1y = mod_add(pre1y, (LL)y * pw1[i] % mod); // +
pre2x = mod_add(pre2x, (LL)x * pw2[i] % mod); // -
pre2y = mod_add(pre2y, (LL)y * pw2[i] % mod); // -
}
printf("%d\n", ans);
}
return 0;
}
逗比小记:
场上拿 ∑ni=1∑nj=i+1∑nk=j+12n−1−i+j−k(fi×fj+fj×fk+fk×fi) 算的,自己折磨自己不说,还拖我队后腿啊。