zoukankan      html  css  js  c++  java
  • 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;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    Gitlab -- 基本操作
    javascript--事件委托
    javascript--Dom 二级事件
    Tableau学习笔记之五
    Tableau学习笔记之二
    Tableau学习笔记之四
    Tableau学习笔记之三
    Tableau学习笔记之一
    Qt使用Cookies对网站操作之Get和POST
    C++ 使用Htmlcxx解析Html内容(VS编译库文件)
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910085.html
Copyright © 2011-2022 走看看