HDU 5448 Marisa’s Cake

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5448

题意:
给定一个由 n 个整点构成的凸多边形,求从n个点里任意选不少于 3 个点组成的凸多边形面积之和,显然整点构成的多边形面积一定是0.5的整数倍,题目需要你算出答案的 2 mod1000000007的值。
n100000 ,坐标 [0,109]

题解:
面积乘 2 的话直接算叉积就好了,例如三个点A,B,C构成的三角形,设从原点到他们的向量为 fA,fB,fC ,那么三角形面积的 2 倍就是fA×fB+fB×fC+fC×fA,同理 k 个点(对应向量按逆时针排序为f1,f2,...,fk)构成的多边形面积的 2 倍等于k1i=1fi×fi+1+fk×f1
直接算每种情况的多边形求面积是 O(n2n) 的,但是我们可以发现计算面积的过程中有很多的重复计算。
对于两个在原多边形上的点 i,j ,我们可以算出它们产生的贡献 fi×fj 在多少个需要算的多边形 M={a0,a1,...,ap1} 的式子里出现。
首先如果 at=i ,那么必须有 a(t+1)modp=j ,也就是说按逆时针顺序在 i j之间的点都不能选,而且为了保证 p3 ,所以按逆时针顺序在 j i这一段的点至少要有一个在多边形 M 上,设j i 之间有c个点,那么对应的方案数就是 2c1 ,于是我们得到了任意两个点之间向量叉积的系数。
于是答案可以被表示为

i=1nj=1i1(2ij11)(fi×fj)+(2ni+j11)(fj×fi)=i=1n(2i1fi)×(j=1i12jfj)+(j=1i12jfj)×(2ni1fi)

如果令
g(x)=i=1x2ifih(x)=i=1x2ifi

答案就是
i=1n(2i1fi)×h(i1)+g(i1)×(2ni1fi)

于是在模意义下预处理 2i 2i ,边利用前缀和的特点维护 g(i),h(i) ,边统计答案就行了,时间复杂度 O(n)

代码:

#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=1nj=i+1nk=j+12n1i+jk(fi×fj+fj×fk+fk×fi) 算的,自己折磨自己不说,还拖我队后腿啊。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值