Luogu P4800 [CEOI2015 Day2]核能國度
阿新 • • 發佈:2020-12-02
本來就是個SB題,結果NT出題人硬是在邊界上大搞心態,白寫了假演算法浪費了一個早上
感覺再多寫100行去寫邊界太NT了,就講一下這道題核心的思路
假設輻射範圍一定在內部,我們可以先求出左上角的點\((x_1,y_1)\)和右下角的點\((x_2,y_2)\)
容易發現此時的貢獻形式是一圈一圈往內增加的,手玩一下我們發現在經過主對角線時貢獻會增加\(b\),經過副對角線時貢獻會減少\(b\)
於是我們可以把對角線的貢獻先差分了來算,然後在每個矩形的左上角和右上角放上\(a\operatorname{mod}b\),在右上角和左下角放上\(-a\operatorname{mod}b\),然後在一起做一邊二維字首和
由於要子矩陣求和,因此我們再做一遍二維字首和即可
然後剛開始想了一個naive的處理邊界的方法,就是把整個矩形擴大到原來的\(3\times 3\)倍,然後炸出去的就在外面改就好了
結果死調了一個早上才發現需要擴充套件的不是矩形而是邊長為\(\max(w,h)\)的正方形,由於這道題偏偏給的是\(w\times h\)的限制,於是直接昇天
看了下題解好像是要單獨處理溢位去的部分,但是非常難寫細節很多,感覺除了搞自己一個下午的心態之外沒什麼積極作用,遂棄了
給份只能過輻射範圍在內部的程式碼跑路了
#include<cstdio> #include<iostream> #define RI register int #define CI const int& #define int long long using namespace std; const int N=200005; int w,h,n,x[N],y[N],a[N],b[N],q; struct Array { int mp[100000000]; inline int* operator [] (CI x) { return mp+(x+(w+1)+1)*(3*h+1); } }p1,p2; inline void DEBUG(void) { puts("Start Debug:"); for (RI i=1,j;i<=w;++i) for (j=1;j<=h;++j) printf("%lld%c",p1[i][j]," \n"[j==h]); puts("End Debug"); } signed main() { RI i,j; for (scanf("%lld%lld%lld",&w,&h,&n),i=1;i<=n;++i) { scanf("%lld%lld%lld%lld",&x[i],&y[i],&a[i],&b[i]); int d=a[i]/b[i]; int x1=x[i]-d,x2=x[i]+d,y1=y[i]-d,y2=y[i]+d,r; if (x1<1&&x2>w&&y1<1&&y2>h) { r=min(1-x1,min(x2-w,min(1-y1,y2-h))); x1+=r; x2-=r; y1+=r; y2-=r; } else r=0; ++x2; ++y2; p1[x1+1][y1+1]+=b[i]; p1[x2][y2]-=b[i]; p2[x1+1][y2-1]-=b[i]; p2[x2][y1]+=b[i]; } for (i=-w;i<=2*w;++i) for (j=-h;j<=2*h;++j) p1[i][j]+=p1[i-1][j-1],p2[i][j]+=p2[i-1][j+1]; for (i=-w;i<=2*w;++i) for (j=-h;j<=2*h;++j) p1[i][j]+=p2[i][j]; for (i=1;i<=n;++i) { int d=a[i]/b[i],x1=x[i]-d,x2=x[i]+d,y1=y[i]-d,y2=y[i]+d,r; if (x1<1&&x2>w&&y1<1&&y2>h) { r=min(1-x1,min(x2-w,min(1-y1,y2-h))); x1+=r; x2-=r; y1+=r; y2-=r; } else r=0; int v=a[i]-(d-r)*b[i]; ++x2; ++y2; p1[x1][y1]+=v; p1[x2][y2]+=v; p1[x1][y2]-=v; p1[x2][y1]-=v; } for (i=-w;i<=2*w;++i) for (j=-h;j<=2*h;++j) p1[i][j]+=p1[i-1][j]+p1[i][j-1]-p1[i-1][j-1]; for (i=-w;i<=2*w;++i) for (j=-h;j<=2*h;++j) p1[i][j]+=p1[i-1][j]+p1[i][j-1]-p1[i-1][j-1]; for (scanf("%lld",&q),i=1;i<=q;++i) { int x1,y1,x2,y2; scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2); int ret=p1[x2][y2]-p1[x1-1][y2]-p1[x2][y1-1]+p1[x1-1][y1-1]; if (ret<0) ret+=(1LL<<63); printf("%lld\n",(int)(1.0L*ret/((x2-x1+1)*(y2-y1+1))+0.5)); } return 0; }
PS:但凡出題人給個\(n,m\le 1000\)之類的都可以過了,現在這樣難寫地一批