见上帝动了恻隐之心,天后也想显示一下慈悲之怀,随即从口袋中取出一块魔术方巾,让身边的美神维纳斯拿到后堂的屏风上去试试,屏风是正方形的,高和宽方向上各划有m条鱼屏风的边平行的直线,平行直线间的距离为1厘米。这2m条直线共有m*m个交点,在某些交点上镶嵌着宝石。若是魔术方巾的边与屏风的边平行且魔术方巾触碰到屏风上镶嵌着的宝石,就将与这些宝石等值的金银送给人们。维纳斯想让魔术方巾触碰到的宝石的价值最多,可要在短短的1秒钟以内解决问题,也感到力不从心,你能帮帮她吗?node
输入文件gem.in的第一行有三个正整数m,n,k,数与数之间用一个空格分隔。其中m为屏风在高和宽方向上被划分出的直线数。魔术方巾为正方形的,它的边长为k厘米。N为屏风上宝石的个数。
接下来的n行,每行三个正整数,依次表示宝石所在直线的行号、列号、宝石的价值,数与数之间用一个空格分隔。算法
输出文件gem.out只有一个正整数,为魔术方巾触碰到的宝石的最大价值总数。spa
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;翻译
这题在比赛的时候就想到了60分的暴力,因此就都来说讲吧code
(因为时间不够,也没实现,仅供参考)这题我主要是想经过二维前缀和的方式去实现,对于每个点,维护一个以该点为右下角的大小为k*k的矩阵价值和。n和m的大小应该勉强能够卡过去排序
考后看题解,上面写着扫描线。我觉得是什么很高深的算法,顿时凉一半。而后听了听大佬解释,我在这就翻译一下。
就是先将点按横坐标排序,而后1~n扫一遍。而由于咱们只须要k*k的矩阵,因此咱们只须要维护当前点到及前面k个点的价值就好了。那如何维护呢?咱们考虑线段树。设第x个点表示纵坐标从x往上k个单位的贡献。而后咱们枚举横坐标时,每次遇到一个点,就对线段树区间修改便可.。ip
#include <cstdio> #include <algorithm> using namespace std; int n,m,k,i,l,pl,pr,b1,b2,ans,bz[200001],tree[200001]; struct node{ int x,y,w; }a[50001]; bool cmp(node a,node b){return a.x<b.x;} void lazy(int x,int y,int z) { if (!bz[z]) return; tree[z]+=bz[z]; if (x==y) { bz[z]=0; return; } bz[z*2]+=bz[z];bz[z*2+1]+=bz[z]; bz[z]=0; } void add(int x,int y,int z,int l,int r,int s) { if (x==l && y==r) { bz[z]+=s; lazy(x,y,z); return; } int mid=(x+y)/2; lazy(x,mid,z*2); lazy(mid+1,y,z*2+1); if (mid>=r) add(x,mid,z*2,l,r,s);else if (mid<l) add(mid+1,y,z*2+1,l,r,s);else { add(x,mid,z*2,l,mid,s); add(mid+1,y,z*2+1,mid+1,r,s); } tree[z]=max(tree[z*2],tree[z*2+1]); } int main() { freopen("gemstone.in","r",stdin); scanf("%d%d%d",&m,&n,&k); for (i=1;i<=n;i++) { scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w); } sort(a+1,a+n+1,cmp); l=1;pl=1;pr=1; for (i=1;i<=m;i++) { if (i>k+1) { while (a[pl].x<=l && pl<=n) { b1=max(a[pl].y-k,1); b2=a[pl].y; lazy(1,m,1); add(1,m,1,b1,b2,-a[pl].w); pl++; } l++; } while (a[pr].x<=i && pr<=n) { b1=max(a[pr].y-k,1); b2=a[pr].y; lazy(1,m,1); add(1,m,1,b1,b2,a[pr].w); pr++; } ans=max(ans,tree[1]); } printf("%d",ans); }