【noi.ac】#283. 唐时月夜

题目

唐时月夜
题目背景
Oh, how beautiful it used to be

Just you and me far beyond the sea,

The waters’ scarce in motion

Quivering still.

题目描述
小X是一名X国的附魔师。

静谧的月夜,水域中倒映着明亮的夜空,这正是水域中魔力最旺盛的时候。小X面前的水域可以被分成N行M列的子水域,每一片子水域存在「魔力值」Ai,j。根据「水域之书」的记载,存在线性递推序列f0=c,fi=a×fi−1+b (i≥1),初始时,「魔力值」矩阵满足
Ai,j=f(i−1)×M+j
小X需要根据「预言」的指示按顺序对这片水域施展Q次魔法,具体来说,「预言」的指示分为以下几种:

1、选定一个矩形的子水域A[x1…x2,y1…y2],对其进行「左右倒置」操作,即对于这个子水域的每一行,翻转「魔力值」序列。

2、选定一个矩形的子水域A[x1…x2,y1…y2],对其进行「上下倒置」操作,即对于这个子水域的每一列,翻转「魔力值」序列。

3、选定一个正方形 的子水域A[x1…x2,y1…y2],对其进行「行列倒置」操作,即沿着这个子水域的主对角线,翻转「魔力值」矩阵。

同时,为了保证施法的连续性,「预言」的指示保证如果一片子水域曾经被操作过,那么在之后的施法中,这片子水域也一定会被操作。

在完成全部的施法后,小X需要根据「魔力值」矩阵,计算出「魔力神谕」,即
N

i=1
M

j=1 Ai,j×f(i−1)×M+j
由于「魔力神谕」可能很大,小X只需要知道「魔力神谕」在模232意义下的数值就可以了。

输入格式
第一行一个整数Num,表示测试点编号,以便选手方便地获得部分分,你可能不需要用到这则信息,样例中Num的含义为数据范围与某个测试点相同。

接下来一行3个整数N,M,Q,分别表示水域的大小,以及「预言」指示的次数。

接下来一行3个整数a,b,c,表示「水域之书」中记载的线性递推序列。

接下来Q行,每行5个整数opt,x1,y1,x2,y2,表示「预言」的一次指示。

输出格式
输出一行一个整数Ans,表示「魔力神谕」在模232意义下的数值。

样例1输入
2
3 4 3
1 1 0
1 2 2 3 3
2 2 2 3 4
3 1 2 3 4
样例1输出
576
样例1解释
初始时,「魔力值」矩阵如下:
( 1 2 3 4 5 6 7 8 9 10 11 12 )
在第1次「预言」的指示后,「魔力值」矩阵如下:
( 1 2 3 4 5 7 6 8 9 11 10 12 )
在第2次「预言」的指示后,「魔力值」矩阵如下:
( 1 2 3 4 5 11 10 12 9 7 6 8 )
在第3次「预言」的指示后,「魔力值」矩阵如下:
( 1 2 11 7 5 3 10 6 9 4 12 8 )
可以计算得到「魔力神谕」在模232意义下的数值为576。

样例2
见下发文件ex_evernight2.in,ex_evernight2.out

点此下载

数据范围与约定
对于所有测试数据,保证1≤N,M≤4×103,0≤Q≤2×105,0≤a,b,c<232,opt∈{1,2,3},1≤x1≤x2≤N,1≤y1≤y2≤M。

对于opt=3的指示,保证x2−x1=y2−y1。

保证如果一片子水域曾经被操作过,那么在之后的施法中,这片子水域也一定会被操作。

详细的数据范围见下表。

在这里插入图片描述

思路

一旦某一个矩形的子水域被操作了,那么其中所有元素的相邻关系就确定下来,我
们只会对其整体进行操作。
那么,我们可以在全局维护这些对当前矩形整体进行的操作的效果,其实质上是一
个对位置的线性变换,在操作不同的矩形的时候将新增的部分拼接上去即可。
时间复杂度 O(N × M + Q) 。
具体实现的时候,笔者采用了一种直观的方式,即维护所有点的相邻关系,以及当
前矩形上、下、左、右边界的有序点集。在操作时,我们只需交换和翻转一些点集即可,
翻转可以通过标记来实现;而这样的实现方式中,拼接的过程将会比较直观。最后,我
们可以依据整个矩形的上、左边界,以及所有点的相邻关系来还原矩阵。
但是,这样的做法将会带来较大的常数,远远不及直接用线性变换的观点来实现本
题,这也是本题时间限制较长的原因。

代码

#include<cstdio>
#include<algorithm>
#define y1 yl
using namespace std;
const int N=4010, M=200010;
int n,m,Q,opt,x1,x2,y1,y2,a[N][N];
unsigned int A,B,ans,f[N*N];
struct jz{int z[3][3];} b[M];
jz operator * (const jz &a,const jz &b) {
	jz c=(jz){{{0,0,0},{0,0,0},{0,0,0}}};
	for(int i=0;i<3;i++)
	for(int j=0;j<3;j++) if (b.z[i][j]!=0)
	for(int k=0;k<3;k++) c.z[i][k]+=b.z[i][j]*a.z[j][k];
	return c;
}
int read() {
	int tmp=0, fh=1; char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') fh=-1; c=getchar();}
	while (c>='0'&&c<='9') tmp=tmp*10+c-48, c=getchar();
	return tmp*fh;
}
int main() {
	read();
	n=read(); m=read(); Q=read();
	scanf("%u%u%u",&A,&B,&f[0]);
	for(int i=1;i<=n*m;i++) f[i]=A*f[i-1]+B;
	for(int q=1;q<=Q;q++) {
		opt=read(); x1=read(); y1=read(); x2=read(); y2=read();
		a[x1][y1]++; a[x2+1][y1]--; a[x1][y2+1]--; a[x2+1][y2+1]++;
		if (opt==1) b[q]=(jz){{{1,0,0},{0,-1,y1+y2},{0,0,1}}};
		if (opt==2) b[q]=(jz){{{-1,0,x1+x2},{0,1,0},{0,0,1}}};
		if (opt==3) b[q]=(jz){{{0,1,x1-y1},{1,0,y1-x1},{0,0,1}}};
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
	for(int q=Q-1;q>0;q--) b[q]=b[q]*b[q+1];
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++) {
		if (!a[i][j]) ans+=f[(i-1)*m+j]*f[(i-1)*m+j];
		else {
			opt=Q-a[i][j]+1;
			int ii=b[opt].z[0][0]*i+b[opt].z[0][1]*j+b[opt].z[0][2];
			int jj=b[opt].z[1][0]*i+b[opt].z[1][1]*j+b[opt].z[1][2];
			ans+=f[(i-1)*m+j]*f[(ii-1)*m+jj];
		}
	}
	printf("%u\n",ans);

} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值