【NOI2015模拟1.17】⑨

Description

Cirno闲着无事的时候喜欢冰冻青蛙。
Cirno每次从雾之湖中固定的n个结点中选出一些点构成一个简单多边形,Cirno运用自己的能力能将此多边形内所有青蛙冰冻。
雾之湖生活着m只青蛙,青蛙有大有小,所以每只青蛙的价值为一个不大于10000的正整数。
Cirno很想知道每次冻住的青蛙的价值总和。因为智商有限,Cirno将这个问题交给完美算术教室里的你。
因为爱护动物,所以每次冻结的青蛙会被放生。也就是说一只青蛙可以被多次统计。
-10000<=x,y<=10000; 0

Solution

计算几何式懵逼
我们知道可以先选择一个原点,然后通过类似叉积的做法求出这个多边形的有向面积
这里的面积定义成内部的点的个数
于是我们只需要求出每个三角形内部的点的权值和,两次排序之后树状数组维护即可。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef double db;
typedef long long ll;

const int N=1e3+5;

int n,m,ans,Q,a[N],ord[N],S,Ans[N<<1][N<<1],tr[N<<1];
db eps=1e-7;

void ins(int x,int y) {for(;x<=n+m;x+=x&-x) tr[x]+=y;}
int query(int x) {int res=0;for(;x;x-=x&-x) res+=tr[x];return res;}
int find(int l,int r) {
    if (l>r) swap(l,r);
    return query(r)-query(l-1);
}

struct P{
    int x,y,v,id,w;db a;
    P(int _x=0,int _y=0) {x=_x;y=_y;v=id=w=a=0;}
    friend P operator + (P x,P y) {return P(x.x+y.x,x.y+y.y);}
    friend P operator - (P x,P y) {return P(x.x-y.x,x.y-y.y);}
}p[N<<1],O,q[N<<1];

int cross(P x,P y) {return x.x*y.y-y.x*x.y;}

int sig(int x) {
    if (x>0) return 1;
    if (x==0) return 0;
    if (x<0) return -1;
}

bool cmp(P x,P y) {return x.a<y.a;}

db theta(P x,P y) {
    db a=x.x*y.x+x.y*y.y;
    a/=sqrt(x.x*x.x+x.y*x.y)*sqrt(y.x*y.x+y.y*y.y);
    return acos(a);
}

int main() {
    scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%d%d",&p[i].x,&p[i].y),p[i].id=i;
    fo(i,1,m) scanf("%d%d%d",&p[i+n].x,&p[i+n].y,&p[i+n].v);
    O=P(-20000,-20000);
    fo(i,1,n+m) p[i].a=atan2(p[i].y-O.y,p[i].x-O.x);
    sort(p+1,p+n+m+1,cmp);
    fo(i,1,n+m) {
        p[i].w=i;
        if (p[i].id) ord[p[i].id]=i;
    }
    fo(i,1,n+m)
        if (!p[i].v) {
            int tot=0;
            fo(j,i+1,n+m) {
                q[++tot]=p[j];
                q[tot].a=theta(p[j]-O,p[i]-O)+theta(O-p[j],p[i]-p[j]);
            }
            sort(q+1,q+tot+1,cmp);
            fo(j,1,tot) 
                if (q[j].v) ins(q[j].w,q[j].v);
                else Ans[i][q[j].w]=Ans[q[j].w][i]=find(q[j].w,p[i].w);
            fo(j,1,tot) if (q[j].v) ins(q[j].w,-q[j].v);
        }
    for(scanf("%d",&Q);Q;Q--) {
        scanf("%d",&S);
        int ans=0;
        fo(i,1,S) {
            scanf("%d",&a[i]);a[i]=ord[a[i]];
            if (i!=1) ans+=sig(cross(p[a[i]]-O,p[a[i-1]]-O))*Ans[a[i]][a[i-1]];
        }
        ans+=sig(cross(p[a[1]]-O,p[a[S]]-O))*Ans[a[1]][a[S]];
        if (ans<0) ans=-ans;
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值