【jzoj1495】宝石 {扫描线+线段树维护+离散化}

题目

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

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

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


解题思路

比赛时,一直认为是离散化,想了很久没能想到如何离散。所以就敲了一篇60分的前缀和(后来改对)。
正解是扫描线+线段树维护+离散化。一,先将每一个点按高从小到大排序。二,用一条宽为k,长为m的长方形从下到上扫描。三,在这个区间里的点,可以按x、往右k的区间,权值+1,这个过程用线段树维护


代码(60分)

#include<cstdio>
#include<algorithm>
using namespace std; 
int m,n,k,a,b,t,c[3001][3001],ans; 
int main()
{
    scanf("%d%d%d",&m,&n,&k); 
    for (int i=1;i<=n;i++) 
    {
     scanf("%d%d%d",&a,&b,&t);  
     c[a][b]=t; 
    }    
    for (int i=1;i<=m;i++)
     for (int j=1;j<=m;j++)
     c[i][j]=c[i][j]+c[i-1][j]+c[i][j-1]-c[i-1][j-1]; 
    for (int i=k+1;i<=m;i++)
     for (int j=k+1;j<=m;j++)
      ans=max(ans,c[i][j]-c[i-k-1][j]-c[i][j-k-1]+c[i-k-1][j-k-1]); 
    printf("%d\n",ans); 
}

代码(100分)

#include<cstdio>
#include<algorithm>
using namespace std; 
struct node{
    int x,y,z; 
}a[50001];
struct nodd{
    int l,r,wo,lazy; 
}b[300001];
int m,n,k,maxs; 
bool cmp(node q,node e)
{ return q.y<e.y; }
void build(int l,int r,int kg)
{
   b[kg].l=l; b[kg].r=r; 
   if (l==r) return; 
   int mid=(l+r)/2; 
   build(l,mid,kg*2); build(mid+1,r,kg*2+1); 
}
void data(int kg)
{
    b[kg*2].wo+=b[kg].lazy; 
    b[kg*2+1].wo+=b[kg].lazy; 
    b[kg*2].lazy+=b[kg].lazy; 
    b[kg*2+1].lazy+=b[kg].lazy; 
    b[kg].lazy=0; 
}
void updata(int l,int r,int kg,int num)
{
    if (b[kg].l==l&&b[kg].r==r) {
        b[kg].wo+=num; b[kg].lazy+=num; 
        return; 
    }
    data(kg); 
    if (b[kg*2].r>=r) updata(l,r,kg*2,num); 
    else if (b[kg*2+1].l<=l) updata(l,r,kg*2+1,num); 
    else { updata(l,b[kg*2].r,kg*2,num); updata(b[kg*2+1].l,r,kg*2+1,num); }
    b[kg].wo=max(b[kg*2].wo,b[kg*2+1].wo); 
}//以上为线段树
int main()
{
    scanf("%d%d%d",&m,&n,&k); 
    for (int i=1;i<=n;i++)
     scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    sort(a+1,a+n+1,cmp); 
    build(1,m,1); 
    int d=1; 
    for (int i=1;i<=n;i++)
    {
       while (a[i].y-a[d].y>k)
       {
         updata(a[d].x,min(a[d].x+k,m),1,-a[d].z); 
         d++; //逐渐往上移
       }
       updata(a[i].x,min(a[i].x+k,m),1,a[i].z); 
       maxs=max(maxs,b[1].wo);
    }
    printf("%d",maxs); 
}

猜你喜欢

转载自blog.csdn.net/qq_39897867/article/details/80975989
今日推荐