[hdu 5032]2014北京网络赛Always Cook Mushroom 离线线段树/树状数组

题目大意

给定一片蘑菇田,只有从(1,1)至(1000,1000)的整数点能产蘑菇,点(x,y)的蘑菇产量为(x+A)(y+B)

给定直角三角形的两个顶点和斜边的斜率,用(a,b)的形式给出,求该三角形内的蘑菇产量和。


解题思路

因为点最多有1000*1000=100W个,可以先求出他们斜率并排序,找到每个斜率的rank,并将询问按照极角排序,离线将询问的斜率也排序,逆时针每个点加入线段树。询问实际上就是求一个前缀和了。一边处理询问,一边处理这些点。

每次区间更新[p,1000](相当于添加一条权值为(x+A)(y+B)的线段),单点查询该点值。

#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
using namespace std;
inline int ReadInt()
{
    int flag=0;
    char ch=getchar();
    int data=0;
    while (ch<'0'||ch>'9')
    {
        if (ch=='-') flag=1;
        ch=getchar();
    }
    do
    {
        data=data*10+ch-'0';
        ch=getchar();
    }while (ch>='0'&&ch<='9');
    return data;
}
int A,B;
struct query
{
    int p,a,b,r;
}q[100005];
LL addv[5005];
LL ans[100005];
struct node
{
    int a,b;
}sl[1000005],tmp;
int cmp2(node x,node y)
{
    return (x.b*y.a<x.a*y.b);
}
int cmp1(query x,query y)
{
    return (x.b*y.a<x.a*y.b);
}
int cmp(query x,node y)
{
    return (x.b*y.a>=x.a*y.b);
}
void update(int o, int L, int R,int le,int ri,LL v)
{
    if (le<=L && ri>=R)
        addv[o]+=v;
    else {
        int M = (L + R)>>1;
        if (le<=M) update(o<<1,L,M,le,ri,v);
        if (ri>M) update(o<<1|1,M+1,R,le,ri,v);
    }
}

LL quer(int p,int x,int L,int R)
{
    int M = (L + R)>>1;
    LL sum=addv[x];
    if (L==R) return sum;
    if (p<=M) sum+=quer(p,x<<1,L,M);
    if (p>M) sum+=quer(p,x<<1|1,M+1,R);
    return sum;
}
int main()
{
    int T,m,ca=0,cnt=0;
    for (int i=1;i<=1000;i++)
        for (int j=1;j<=1000;j++)
        {
            sl[++cnt].a=i;
            sl[cnt].b=j;
        }
    sort(sl+1,sl+1+cnt,cmp2);
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&A,&B);
        scanf("%d",&m);
        for (int i=1;i<=m;i++)
        {
            q[i].r=i;
            q[i].a=ReadInt();
            q[i].b=ReadInt();
            q[i].p=ReadInt();
        }
        sort(q+1,q+1+m,cmp1);
        memset(addv,0,sizeof addv);
        int pt=0;
        for (int i=1;i<=m;i++)
        {
            while (pt<1000000&&cmp(q[i],sl[pt+1]))
            {
                pt++;
                update(1,1,1000,sl[pt].a,1000,(sl[pt].a+A)*(sl[pt].b+B));
            }
            ans[q[i].r]=quer(q[i].p,1,1,1000);
        }
        printf("Case #%d:\n",++ca);
        for (int i=1;i<=m;i++)
            printf("%I64d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值