見上帝動了惻隱之心,天后也想顯示一下慈悲之懷,隨即從口袋中取出一塊魔術方巾,讓身邊的美神維納斯拿到後堂的屏風上去試試,屏風是正方形的,高和寬方向上各劃有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); }