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\)之类的都可以过了,现在这样难写地一批

PPS:陈指导的正解

posted @ 2020-12-02 11:51  空気力学の詩  阅读(138)  评论(0编辑  收藏  举报