hdu 4533(一种很巧妙的方法|线段树+扫描线)

比赛的时候想了很久,但是没有想出来。 这题用线段树思路应该挺好想, 但是会复杂一些,而用这个方法简单又好写,但是确实比较难想到。。

大牛的解题报告,很详细   http://blog.csdn.net/wh2124335/article/details/8739097

威威猫系列故事——晒被子

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 772    Accepted Submission(s): 184


Problem Description
  因为马拉松初赛中吃鸡腿的题目让不少人抱憾而归,威威猫一直觉得愧对大家,这几天他悄悄搬到直角坐标系里去住了。
  生活还要继续,太阳也照常升起,今天,威威猫在第一象限晒了N条矩形的被子,被子的每条边都和坐标轴平行,不同被子的某些部分可能会叠在一起。这时候,在原点处突然发了场洪水,时间t的时候,洪水会蔓延到( t, t ),即左下角为( 0, 0 ) ,右上角为( t, t )的矩形内都有水。
  悲剧的威威猫想知道,在时间t1, t2, t3 ... tx 的时候,他有多少面积的被子是湿的?
 
Input
输入数据首先包含一个正整数T,表示有T组测试数据;
每组数据的第一行首先是一个整数N,表示有N条被子;
接下来N行,每行包含四个整数x1, y1, x2, y2,代表一条被子的左下角和右上角的坐标;
然后接下来一行输入一个整数x,表示有x次询问;
再接下来x行,输入x个严格单调递增的整数,每行一个,表示威威猫想知道的时间ti。

[Technical Specification]
T <= 5
0 < N <= 20000
1 <= x1 < x2 <= 200000
1 <= y1 < y2 <= 200000
1 <= x <= 20000
1 <= ti <= 200000 (1 <= i <= x )
 
Output
对于每次询问,请计算并输出ti时有多少面积的被子是湿的,每个输出占一行。
 
Sample Input
1 2 1 1 3 3 2 2 4 4 5 1 2 3 4 5
 
Sample Output
0 1 5 8 8
 
Source
 
Recommend
liuyiding
 
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
#define N 50020

typedef __int64 LL;

struct node
{
    LL x,y,key;
    LL sum,sum1;
}g[N],g1[N];

int n;
int cnt,cnt1;

int cmp(node t,node t1)
{
    return t.key<t1.key;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(g,0,sizeof(g));
        memset(g1,0,sizeof(g1));
        cnt=0; cnt1=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            node tmp1,tmp2,tmp3,tmp4;
            LL x,y;
            scanf("%I64d%I64d",&x,&y);
            tmp1.x=x; tmp1.y=y; tmp1.key=max(x,y); tmp1.sum=0; tmp1.sum1=0;
            scanf("%I64d%I64d",&x,&y);
            tmp2.x=x; tmp2.y=y; tmp2.key=max(x,y); tmp2.sum=0; tmp2.sum1=0;
            x=max(tmp1.x,tmp2.x);
            y=min(tmp1.y,tmp2.y);
            tmp3.x=x; tmp3.y=y; tmp3.key=max(x,y); tmp3.sum=0; tmp3.sum1=0;
            x=min(tmp1.x,tmp2.x);
            y=max(tmp1.y,tmp2.y);
            tmp4.x=x; tmp4.y=y; tmp4.key=max(x,y); tmp4.sum=0; tmp4.sum1=0;
            g1[cnt1++]=tmp3; g1[cnt1++]=tmp4;
            g[cnt++]=tmp1; g[cnt++]=tmp2;
        }
        sort(g,g+cnt,cmp);
        sort(g1,g1+cnt1,cmp);
        LL tmp=0,tmp1=0; 
        for(int i=0;i<cnt;i++)
        {
            tmp=tmp+(g[i].x+g[i].y);
            tmp1=tmp1+(g[i].x*g[i].y);
            g[i].sum=tmp; g[i].sum1=tmp1;
        }
        tmp=0; tmp1=0;
        for(int i=0;i<cnt1;i++)
        {
            tmp=tmp+g1[i].x+g1[i].y;
            tmp1=tmp1+(g1[i].x*g1[i].y);
            g1[i].sum=tmp; g1[i].sum1=tmp1;
        }
        int q;
        scanf("%d",&q);
        for(int i=0;i<q;i++)
        {
            LL ans=0;
            LL ss;
            scanf("%I64d",&ss);
            LL b=0,d=cnt-1;
            if(cnt==0||ss<=g[0].key) ans=0; 
            else
            {
                while(b<d)
                {
                    int mid=(b+d+1)/2;
                    if(ss<=g[mid].key)
                        d=mid-1;
                    else b=mid;
                }
                ans=ans+(b+1)*ss*ss-ss*(g[b].sum)+g[b].sum1;
            }
            b=0; d=cnt1-1;
            if(cnt1!=0&&ss>g1[0].key) 
            {
                while(b<d)
                {
                    int mid=(b+d+1)/2;
                    if(ss<=g1[mid].key) d=mid-1;
                    else b=mid;
                }
                ans=ans-((b+1)*ss*ss-ss*(g1[b].sum)+g1[b].sum1);
            }
            printf("%I64d\n",ans);
        }
    }
    return 0;
}

最近学了线段树+扫描线,发现这题也可以用这种方法来做。坑爹的是这题竟然卡时间卡的这么紧,我姿势各种不优美于是各种被卡。 逼得我用离散化。。 结果还是980ms飘过。。。坑啊

其实统一复杂度的何必卡的这么死。。。

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <map>
#include <stdlib.h>
using namespace std;
#define N 20020
#define SN 1000000

struct LINE
{
    int k;
    int b,d;
    int flag;
}line[10*N];

int n;
int l[SN],r[SN];
__int64 flags[SN],flagn[SN];
__int64 sum[SN],num[SN];
int save[N*10];
__int64 ganl[N*10],ganr[N*10];
int cao[200200];

int cmp(LINE t,LINE t1)
{
    return t.k<t1.k;
}

void build(int tl,int tr,int s)
{
    l[s]=tl; r[s]=tr;
    sum[s]=0; num[s]=0; flags[s]=0; flagn[s]=0;
    if(tl==tr) return ;
    int mid=(tl+tr)/2;
    build(tl,mid,2*s);
    build(mid+1,tr,2*s+1);
}
void down(int s)
{
    if(flags[s]!=0)
    {
        flags[2*s]+=flags[s];
        sum[2*s] += flags[s]*(r[2*s]-l[2*s]);

        flags[2*s+1]+=flags[s];
        sum[2*s+1] += flags[s]*(r[2*s+1]-l[2*s+1]);
        flags[s]=0;
    }
    if(flagn[s]!=0)
    {
        flagn[2*s]+=flagn[s];
        num[2*s] += flagn[s]*(r[2*s]-l[2*s]);

        flagn[2*s+1]+=flagn[s];
        num[2*s+1] += flagn[s]*(r[2*s+1]-l[2*s+1]);
        flagn[s]=0;
    }
}

void up(int s)
{
    sum[s]=sum[2*s]+sum[2*s+1];
    num[s]=num[2*s]+num[2*s+1];
}


void update(int tl,int tr,int x,int sign,int s)
{
    if(tl==l[s]&&tr==r[s])
    {
        //从tl到tr这段线段,同时加
        flags[s] += -sign*x;
        sum[s] += -sign*x*(ganr[tr]-ganl[tl]);
        //然后就是记录长度
        flagn[s] += sign;
        num[s] += sign*(ganr[tr]-ganl[tl]);
        return ;
    }
    //down(s);//肯定要下推
    if(flags[s]!=0)
    {
        flags[2*s]+=flags[s];
        sum[2*s] += flags[s]*(ganr[r[2*s]]-ganl[l[2*s]]);

        flags[2*s+1]+=flags[s];
        sum[2*s+1] += flags[s]*(ganr[r[2*s+1]]-ganl[l[2*s+1]]);
        flags[s]=0;
    }
    if(flagn[s]!=0)
    {
        flagn[2*s]+=flagn[s];
        num[2*s] += flagn[s]*(ganr[r[2*s]]-ganl[l[2*s]]);

        flagn[2*s+1]+=flagn[s];
        num[2*s+1] += flagn[s]*(ganr[r[2*s+1]]-ganl[l[2*s+1]]);
        flagn[s]=0;
    }

    int mid=(l[s]+r[s])/2;
    if(tr<=mid) update(tl,tr,x,sign,2*s);
    else if(tl>mid) update(tl,tr,x,sign,2*s+1);
    else
    {
        update(tl,mid,x,sign,2*s);
        update(mid+1,tr,x,sign,2*s+1);
    }
    //up(s);
    sum[s]=sum[2*s]+sum[2*s+1];
    num[s]=num[2*s]+num[2*s+1];
}

__int64 query(int tl,int tr,int x,int s)
{
    if(tl==l[s]&&tr==r[s])
    {
        return sum[s]+x*num[s];
    }
    if(flags[s]!=0)
    {
        flags[2*s]+=flags[s];
        sum[2*s] += flags[s]*(ganr[r[2*s]]-ganl[l[2*s]]);

        flags[2*s+1]+=flags[s];
        sum[2*s+1] += flags[s]*(ganr[r[2*s+1]]-ganl[l[2*s+1]]);
        flags[s]=0;
    }
    if(flagn[s]!=0)
    {
        flagn[2*s]+=flagn[s];
        num[2*s] += flagn[s]*(ganr[r[2*s]]-ganl[l[2*s]]);

        flagn[2*s+1]+=flagn[s];
        num[2*s+1] += flagn[s]*(ganr[r[2*s+1]]-ganl[l[2*s+1]]);
        flagn[s]=0;
    }

    int mid=(l[s]+r[s])/2;
    if(tr<=mid) return query(tl,tr,x,2*s);
    else if(tl>mid) return query(tl,tr,x,2*s+1);
    else
    {
        return query(tl,mid,x,2*s)+query(mid+1,tr,x,2*s+1);
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int cnt=0;
        int tcnt=0;
        for(int i=0;i<n;i++)
        {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            line[cnt].k=x1;

            line[cnt].b=y1;
            line[cnt].d=y2;
            line[cnt].flag=1;
            //
            save[tcnt++]=y1;
            save[tcnt++]=y2;
            cnt++;

            line[cnt].k=x2;
            line[cnt].b=y1;
            line[cnt].d=y2;
            line[cnt].flag=-1;
            cnt++;
        }
        int m;
        scanf("%d",&m);
        save[tcnt++]=0;
        for(int i=0;i<m;i++)
        {
            int x;
            scanf("%d",&x);
            line[cnt].k=x;
            line[cnt].b=0;
            line[cnt].d=x;
            save[tcnt++]=x;
            line[cnt].flag=0;
            cnt++;
        }

        sort(save,save+tcnt);

        int pre=0;
        int id=0;
        save[0]=0;
        cao[0]=0;
        id=1;
        for(int i=1;i<tcnt;i++)
        {
            if(save[i]!=pre)
            {
                ganl[id-1]=pre;
                ganr[id-1]=save[i];

                save[id]=save[i];
                cao[save[i]]=id;
                id++;
                pre=save[i];
            }
        }

        for(int i=0;i<cnt;i++)
        {
            line[i].b=cao[line[i].b];
            line[i].d=cao[line[i].d]-1;
        }

        sort(line,line+cnt,cmp);
        //好像直接用区间求和就行了
        build(0,id-1,1);
        //没有关系
        for(int i=0;i<cnt;i++)
        {
            if(line[i].flag!=0)//添加线段
            {
                update(line[i].b,line[i].d,line[i].k,line[i].flag,1);
            }
            else
            {
                //query
                __int64 ans=query(line[i].b,line[i].d,line[i].k,1);
                printf("%I64d\n",ans);
            }
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/chenhuan001/p/2994755.html