题意:
给出长度为L的序列A,根据这个序列跑出一个矩阵,问左上角的点和右下角的点形成的矩阵的和。
思路:
打表可以打出规律,会发现有循环节。比赛的时候发现了,然而写法不好,就GG了。所以写之前一定要想出一个优雅一点的写法啊。
根据这张图可以看出,蓝色框子矩阵的和由那些小的矩阵组成,所以整张图中间的子矩阵可以用容斥原理表示成这些图的左上角的矩阵的运算之和。所以预处理一下二维前缀和。
这样子就好写多了。
注意打表的那一块多打一点,我直接找的具体的数字,似乎是不对的啊。
代码:
typedef long long ll;
struct point
{
ll x,y;
}l,r;
ll len[15]= {0,1,4,3,8,10,12,14,16,18,20};
ll A[25];
ll g[1010][1010];
ll pre[10101][1010];
int L;
ll cal(ll x,ll y)
{
ll xx=(x+1)/L,yy=(y+1)/L;
ll tot=0;
tot+=(xx*yy)*pre[L-1][L-1];
ll xm=(x+1)%L,ym=(y+1)%L;
if(ym!=0)tot+=xx*pre[L-1][ym-1];
if(xm!=0)tot+=yy*pre[xm-1][L-1];
if(xm!=0&&ym!=0)tot+=pre[xm-1][ym-1];
return tot;
}
int main()
{
//freopen("C:/Users/lenovo/Desktop/e.in","r",stdin);
//freopen("C:/Users/lenovo/Desktop/out.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i=0; i<n; i++)
scanf("%lld",&A[i]);
L=len[n];
//L=2ll*n;
memset(pre,0,sizeof(pre));
int cursor=0;
for(int i=0; i<100; ++i)
{
for(int j=0; j<=i; ++j)
{
g[j][i-j]=A[cursor];
cursor=(cursor+1)%n;
}
}
/*for(int i=0;i<L+1;i++)
{
for(int j=0;j<L+1;j++)
printf("%3d",g[i][j]);
printf("\n");
}
printf("\n");*/
ll cur;
for(int i=0;i<L;i++)
{
cur=0;
for(int j=0;j<L;j++)
{
cur+=g[i][j];
if(i==0)pre[i][j]=cur;
else pre[i][j]=cur+pre[i-1][j];
}
}
/*for(int i=0;i<L+1;i++)
{
for(int j=0;j<L+1;j++)
printf("%4d",pre[i][j]);
printf("\n");
}
printf("\n");*/
int q;
scanf("%d",&q);
while(q--)
{
scanf("%lld%lld%lld%lld",&l.x,&l.y,&r.x,&r.y);
//printf("(%lld,%lld)(%lld,%lld)(%lld,%lld)(%lld,%lld)\n",r.x,r.y,l.x-1,l.y-1,l.x-1,r.y,r.x,l.y-1);
//printf("%lld %lld %lld %lld\n",cal(r.x,r.y),cal(l.x-1,l.y-1),cal(l.x-1,r.y),cal(r.x,l.y-1));
printf("%lld\n",cal(r.x,r.y)+cal(l.x-1,l.y-1)-cal(l.x-1,r.y)-cal(r.x,l.y-1));
}
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!