zoukankan      html  css  js  c++  java
  • Luogu P4800 [CEOI2015 Day2]核能国度

    本来就是个SB题,结果NT出题人硬是在边界上大搞心态,白写了假算法浪费了一个早上

    感觉再多写100行去写边界太NT了,就讲一下这道题核心的思路

    假设辐射范围一定在内部,我们可以先求出左上角的点((x_1,y_1))和右下角的点((x_2,y_2))

    容易发现此时的贡献形式是一圈一圈往内增加的,手玩一下我们发现在经过主对角线时贡献会增加(b),经过副对角线时贡献会减少(b)

    于是我们可以把对角线的贡献先差分了来算,然后在每个矩形的左上角和右上角放上(aoperatorname{mod}b),在右上角和左下角放上(-aoperatorname{mod}b),然后在一起做一边二维前缀和就可以求出每个格子的答案

    由于要子矩阵求和,因此我们再做一遍二维前缀和即可

    然后刚开始想了一个naive的处理边界的方法,就是把整个矩形扩大到原来的(3 imes 3)倍,然后炸出去的就在外面改就好了

    结果死调了一个早上才发现需要扩展的不是矩形而是边长为(max(w,h))的正方形,由于这道题偏偏给的是(w imes 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]," 
    "[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
    ",(int)(1.0L*ret/((x2-x1+1)*(y2-y1+1))+0.5));
    	}
    	return 0;
    }
    

    PS:但凡出题人给个(n,mle 1000)之类的都可以过了,现在这样难写地一批

    PPS:陈指导的正解

  • 相关阅读:
    Docker学习(一)
    mysql定时任务
    如何查看电脑是几核的?
    卸载Mysql connect 6.9.9
    找不到该项目怎么删除
    jmeter录制app脚本
    postman使用
    排查linux系统是否被入侵
    部署apache-tomcat环境
    sudo初级授权设置
  • 原文地址:https://www.cnblogs.com/cjjsb/p/14072919.html
Copyright © 2011-2022 走看看