JZOJ 3162. 【GDOI2013模拟2】旋转(推式子+差分)

JZOJ 3162. 【GDOI2013模拟2】旋转 (Standard IO)

题目

Description

Alice和Bob发明了一个新的旋转游戏。首先,Bob给定 N N N个数组成的序列,并把该序列平均分配成若干个块,每块正好包含 K K K个数( K K K能整除 N N N)。第一块由第 1 1 1到第 K K K个数构成,第二块由第 K + 1 K+1 K+1个数到第 2 K 2K 2K个数构成,以此类推。
接着,Bob要求Alice对这个序列进行一系列操作,操作有以下两种:
1.把每块里面的数向左或右旋转 X X X个位置;
2.把整个序列向左或向右旋转 X X X个位置。
注意操作2会改变每一块里面的数。
在执行完一系列操作后,Alice把最终的序列告诉了Bob。Bob的任务就是找到初始序列。

Input

第一行输入三个正整数:序列的长度 N ( 1 ≤ N ≤ 100 , 000 ) N(1≤N≤100,000) N(1N100,000),每一块的大小 K ( 1 ≤ K ≤ 100 , 000 ) K(1≤K≤100,000) K(1K100,000),操作次数 Q ( 1 ≤ Q ≤ 100 , 000 ) Q(1≤Q≤100,000) Q(1Q100,000)
接下来 Q Q Q行,每行包含两个整数:操作种类 A ( 1 ≤ A ≤ 2 ) A(1≤A≤2) A(1A2)和旋转位数 X ( − 100 , 000 ≤ X ≤ 100 , 000 ) X(-100,000≤X≤100,000) X(100,000X100,000),其中负数表示向左旋转,整数表示向右旋转。
最后一行输入 N N N个数 Z i ( 0 ≤ Z i ≤ 100 , 000 ) Z_i(0≤Z_i≤100,000) Zi(0Zi100,000)表示最终序列。

Output

一行输出初始序列。

Sample Input

输入1:
4 2 2
2 2
1 1
3 2 1 0

输入2:
8 4 4
1 3
1 15
1 -5
2 -1
6 10 14 19 2 16 17 1

输入3:
9 3 5
1 1
2 -8
2 9
1 1
2 -4
3 1 8 7 4 5 2 6 9

Sample Output

输出1:
0 1 2 3

输出2:
6 10 14 1 2 16 17 19

输出3:
5 3 6 9 7 1 8 2 4

Data Constraint

40%的数据满足 N ≤ 100 N≤100 N100.
70%的数据满足 K ≤ 100 K≤100 K100.

题解

  • 显然这题需要从后往前退,方向全部相反。
  • 如果每个位置都进行一遍计算,时间复杂度是 O ( N K ) O(NK) O(NK)的,时超!
  • 不妨把 N N N个数排成 K K K行的形式,
  • 1 − 12 1-12 112的排列, K = 3 K=3 K=3
  • 1 4 7 10
  • 2 5 8 11
  • 3 6 9 12
  • 若进行 1 1 1操作, X = 2 X=2 X=2
  • 2 5 8 11
  • 3 6 9 12
  • 1 4 7 10
  • 若进行 2 2 2操作, X = 7 X=7 X=7
  • 6 9 12 3
  • 7 10 1 4
  • 8 11 2 5
  • 观察可以发现,
  • 不管怎么操作,原本在同一行的数还在同一行,而且顺序是一定的,只是开头的位置变了。
  • 如1 4 7 10进行 2 2 2操作后从第一行到了第二行,顺序变成7 10 1 4.
  • 行与行之间的顺序也是一定的,只是第一行的位置变了。
  • 既然如此。我们如果知道了第一行的位置和每一行开头的位置,就可以推出整个矩阵,从而得到答案。
  • 现在来分情况讨论:
  • 操作 1 1 1
  • 很显然,不会影响每一行开头的位置,第一行的位置增加 X X X(注意是循环的,到了第 K K K行以后就是第一行)。
  • 操作 2 2 2
  • 第一行的位置也是增加了 X X X
  • 那么每一行的开头呢?
  • 情况一:如果 K ∣ X K|X KX,那么开头位置集体右移 X / K X/K X/K位,
  • 如上面的例子, X = 6 X=6 X=6
  • 7 10 1 4
  • 8 11 2 5
  • 9 12 3 6
  • (是不是非常整齐!!!)
  • 情况二:否则前 X m o d    K Xmod K XmodK行右移 X / K + 1 X/K+1 X/K+1位,其余右移 X / K X/K X/K位,
  • 如上面的例子, X = 2 X=2 X=2
  • 11 2 5 8
  • 12 3 6 9
  • 1 4 7 10
  • (先把第一行的位置变到了第三行,然后前两行开头右移了一位,其余右移了零位。
  • 但不能每一行地去更新开头的位置,
  • 因为每次都是一段区间的修改,所以可以用差分。
  • 具体细节简单推一下!

代码

#include<cstdio>
#include<cstring>
using namespace std;
#define N 100010
struct
{
	int x,y;
}a[N];
int b[N],c[N],d[N],ans[N];
int main()
{
	int n,k,q,i,j,x,y;
	scanf("%d%d%d",&n,&k,&q);
	for(i=1;i<=q;i++) 
	{
		scanf("%d%d",&a[i].x,&a[i].y);
		a[i].y=-a[i].y;
		if(a[i].x==1) a[i].y=(a[i].y%k+k)%k; else a[i].y=(a[i].y%n+n)%n;
	}
	for(i=1;i<=n;i++) scanf("%d",&b[i]);
	int fi=1;
	for(i=q;i;i--)
	{
		x=a[i].x,y=a[i].y;
		fi=(fi+y-1)%k+1;
		if(x==2)
		{
			int l=y/k,t=y%k;
			if(t)
			{
				int h=(k-fi+1)%k+1;
				
 				if(h+t-1<=k)
				{
					c[h+t]--;
					c[1]+=l;
					c[h]++;
				}
				else
				{
					c[1]+=l+1;
					c[(h+t)%k]--;
					c[h]++;
				}
			}
			else c[1]+=l;
		}
	}
	d[0]=0;
	for(i=1;i<=k;i++) d[i]=((d[i-1]+c[i])%(n/k)+n/k)%(n/k);
	int l=fi;
	for(i=1;i<=k;i++)
	{
		for(j=0;j<n/k;j++) 
			ans[((d[i]+j)%(n/k))*k+l]=b[i+j*k];
		l=l%k+1;
	}
	for(i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910085.html