1. 程式人生 > 實用技巧 >Luogu P4800 [CEOI2015 Day2]核能國度

Luogu P4800 [CEOI2015 Day2]核能國度

本來就是個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\)之類的都可以過了,現在這樣難寫地一批