码迷,mamicode.com
首页 > 其他好文 > 详细

jzoj1495 宝石 解题报告[扫描线]

时间:2018-07-07 22:40:49      阅读:232      评论:0      收藏:0      [点我收藏+]

标签:恻隐之心   span   离散化   方向   线段树   移动   namespace   etc   getc   

Description

见上帝动了恻隐之心,天后也想显示一下慈悲之怀,随即从口袋中取出一块魔术方巾,让身边的美神维纳斯拿到后堂的屏风上去试试,屏风是正方形的,高和宽方向上各划有m条鱼屏风的边平行的直线,平行直线间的距离为1厘米。这2m条直线共有m*m个交点,在某些交点上镶嵌着宝石。如果魔术方巾的边与屏风的边平行且魔术方巾触碰到屏风上镶嵌着的宝石,就将与这些宝石等值的金银送给人们。维纳斯想让魔术方巾触碰到的宝石的价值最多,可要在短短的1秒钟之内解决问题,也感到力不从心,你能帮帮她吗?

Input

输入文件gem.in的第一行有三个正整数m,n,k,数与数之间用一个空格分隔。其中m为屏风在高和宽方向上被划分出的直线数。魔术方巾为正方形的,它的边长为k厘米。N为屏风上宝石的个数。
接下来的n行,每行三个正整数,依次表示宝石所在直线的行号、列号、宝石的价值,数与数之间用一个空格分隔。

Output

输出文件gem.out只有一个正整数,为魔术方巾触碰到的宝石的最大价值总数。

Sample Input

10    4    4
1     1    9
2     3    5
6     2    12
4     5    6

Sample Output

23

Hint

【输入样例2】
10 4 3
1 1 9
2 3 5
6 2 12
4 5 6
【输出样例2】
18

【数据限制】
30%的数据,1≤m≤500,1≤n≤10000,1≤k≤100;
60%的数据,1≤m≤3000,1≤n≤10000,1≤k≤1000;
100%的数据,1≤m≤50000,1≤n≤50000,1≤k≤10000;

题目大意就是在一个正方形内给定一些点,用一个小正方形去覆盖,由于每个点的分数不同,因此肯定有所谓最大得分,算出最大得分就是我们的任务

我们放正方形,有一个点放在正方形的一角上是最优的,这里我们采用左下角

把输入的点按照x离散化,再按照y进行排序,考虑从下到上扫描线。线怎么来呢?我们把每个点置于小矩形的左下角,每个点就拥有了一个对应的范围,对于每一个范围我们把上边界所在直线进行扫描。

想象各点范围相交的地方肯定是这些点的分数都可以取到的。

在向上扫描的过程中,我们相当于是将一个长为m的矩形(离散化之后就是cnt),宽为k的矩形不断想上移动,然后去其中长为k的得分最大的一段。具体实现就是在扫描线的线段树上维护区间最大值就可以了。

下面附上代码:

#include<algorithm>
#include<cstdio>
using namespace std;

const int maxn=5e4+15;
int m,n,k,tot,cnt;
int left[maxn<<4],right[maxn<<4];
struct STAR
{
    int id,x,y,l;
    bool fl;
}a[maxn<<4],b[maxn<<4];
struct node
{
    int l,r,m,lazy;
}t[maxn<<4];
inline int read()
{
    char ch=getchar();
    int s=0,f=1;
    while (!(ch>=0&&ch<=9)) {if (ch==-) f=-1;ch=getchar();}
    while (ch>=0&&ch<=9) {s=(s<<3)+(s<<1)+ch-0;ch=getchar();}
    return s*f;
}
bool cmp(STAR a,STAR b) {return a.x<b.x;}
bool CMP(STAR a,STAR b) {if (a.y==b.y) return a.l>b.l;else return a.y<b.y;}
void build(int u,int l,int r)
{
    t[u].l=l;
    t[u].r=r;
    t[u].lazy=t[u].m=0;
    if (l==r) return;
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
}
void down(int u)
{
    if (t[u].lazy)
    {
        int k=t[u].lazy;
        t[u].lazy=0;
        t[u<<1].lazy+=k;
        t[u<<1|1].lazy+=k;
        t[u<<1].m+=k;
        t[u<<1|1].m+=k;
    }
}
void add(int u,int l,int r,int k)
{
    if (t[u].l>=l&&t[u].r<=r) {
        t[u].m+=k;
        t[u].lazy+=k;
        down(u);
        return;
    }
    down(u);
    int mid=t[u].l+t[u].r>>1;
    if (l<=mid) add(u<<1,l,r,k);
    if (r>mid) add(u<<1|1,l,r,k);
    t[u].m=max(t[u<<1].m,t[u<<1|1].m);
    return;
}
int query(int u,int l,int r)
{
    if (t[u].l>=l&&t[u].r<=r) return t[u].m;
    down(u);
    int ans=0,mid=t[u].l+t[u].r>>1;
    if (l<=mid) ans=query(u<<1,l,r);
    if (r>mid) ans=max(ans,query(u<<1|1,l,r)); 
    return ans;
}
int main()
{
    m=read();n=read();k=read();
    for (int i=1;i<=n;i++)//a数组用来离散化 
    {
        a[i].x=read();a[i].y=read();a[i].l=read();
        a[i].id=i;
        a[i].fl=0;
        b[i].y=a[i].y;//扫描线左端点 
        b[i].id=i;
        b[i].l=a[i].l;
    }
    tot=n;
    for (int i=1;i<=n;i++)
    {
        a[++tot].id=i;
        a[tot].x=a[i].x+k;
        a[tot].fl=1;
    }
    sort(a+1,a+1+tot,cmp);//按x排序 
    for (int i=1;i<=tot;i++)
    {
        int tt=0;
        if (a[i].x==a[i-1].x&&i!=1) tt=cnt;
        else tt=++cnt;
        if (!a[i].fl) left[a[i].id]=tt;
        else right[a[i].id]=tt;
    }
    tot=n;
    for (int i=1;i<=n;i++)
    {
        b[++tot].id=b[i].id;//扫描线右端点 
        b[tot].l=-b[i].l;
        b[tot].y=b[i].y+k;    
    }
    sort(b+1,b+1+tot,CMP);//按y排序 
    build(1,1,cnt);
    int ans=0;
    for (int i=1;i<=tot;i++)
    {
        if (b[i].l>0) {
            int sum=query(1,left[b[i].id],right[b[i].id]);//只有在b[i]的范围内才能加上b[i].l 
            if (sum+b[i].l>ans) ans=b[i].l+sum;
        }
        add(1,left[b[i].id],right[b[i].id],b[i].l);
    }
    printf("%d",ans);
    return 0;
}

 

jzoj1495 宝石 解题报告[扫描线]

标签:恻隐之心   span   离散化   方向   线段树   移动   namespace   etc   getc   

原文地址:https://www.cnblogs.com/xxzh/p/9278351.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!