【AGC031D】 A Sequence of Permutations (线性代数,找规律)

题面

🔗
在这里插入图片描述
N ≤ 1 0 5 , K ≤ 1 0 9 N\leq 10^5,K\leq 10^9 N105,K109

题解

我们定义两个矩阵 A , B A,B A,B 代表排列 p , q p,q p,q ,具体地, A i , p i = 1 , B i , q i = 1 A_{i,p_i}=1,B_{i,q_i}=1 Ai,pi=1,Bi,qi=1 ,那么, f ( p , q ) f(p,q) f(p,q) 的矩阵就是 A T B A^{T}B ATB A T A^{T} AT A A A 的转置矩阵,由排列的性质可得 A T A A^{T}A ATA 等于 “1” 矩阵。

为了简化推导,我们用 A,B,a,b 分别表示 A , B , A T , B T A,B,A^{T},B^{T} A,B,AT,BT 。现在我们把前面几项列出来找规律。

A

 B
 aB
b a B

bA b aB
bA Ab aB
bAB A baB

bABa B AbaB
bABa aB AbaB
bABab a BAbaB
......

可以发现,长度变化很有规律,每三个连续加一,然后加二,从第二个开始长度依次是 1 , 2 , 3 , 5 , 6 , 7 , 9 , 10 , 11 , . . . . . . 1,2,3,5,6,7,9,10,11,...... 1,2,3,5,6,7,9,10,11,......

最中心的一到两个矩阵依次是 A , B , a B , a , b , A b A,B,aB,a,b,Ab A,B,aB,a,b,Ab 循环;

剩下的部分,左边是 bABa 重复拼接无穷次的前缀,右边是左边部分的置换。

所以我们用“排列变换”快速幂处理左边和右边,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<unordered_map>
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast")
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PR pair<int,int>
// #define int LL
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
// #define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int A[MAXN],B[MAXN],a[MAXN],b[MAXN];
int t4[MAXN],mi[MAXN],pr[MAXN],tl[MAXN];
void revp(int *A,int *B) {
    for(int i = 1;i <= n;i ++) B[A[i]] = i;
    return ;
}
void mult(int *A,int *B,int *C) {
    for(int i = 1;i <= n;i ++) {
        C[i] = B[A[i]];
    } return ;
}
int main() {
    n = read(); m = read();
    for(int i = 1;i <= n;i ++) {
        A[i] = read();
        a[A[i]] = i;
    }
    for(int i = 1;i <= n;i ++) {
        B[i] = read();
        b[B[i]] = i;
    }
    if(m == 1) {
        for(int i = 1;i <= n;i ++) AIput(A[i],i==n ? '\n':' ');
        return 0;
    }
    int ty = m % 6,le = (m-2)/3 + m-1;
    if(le & 1) le = le/2;
    else le = le/2-1;
    if(ty == 1) for(int i = 1;i <= n;i ++) mi[i] = A[i];
    else if(ty == 2) for(int i = 1;i <= n;i ++) mi[i] = B[i];
    else if(ty == 3) mult(a,B,mi);
    else if(ty == 4) for(int i = 1;i <= n;i ++) mi[i] = a[i];
    else if(ty == 5) for(int i = 1;i <= n;i ++) mi[i] = b[i];
    else mult(A,b,mi);
    mult(b,A,t4); mult(B,a,tl);
    mult(t4,tl,t4);
    int nm = (le>>2),el = le&3;
    for(int i = 1;i <= n;i ++) pr[i] = i;
    while(nm > 0) {
        if(nm & 1) mult(pr,t4,pr);
        for(int i = 1;i <= n;i ++) tl[i] = t4[i];
        mult(t4,tl,t4); nm >>= 1;
    }
    if(el >= 1) mult(pr,b,pr);
    if(el >= 2) mult(pr,A,pr);
    if(el >= 3) mult(pr,B,pr);
    revp(pr,tl);
    mult(pr,mi,pr); mult(pr,tl,pr);
    for(int i = 1;i <= n;i ++) AIput(pr[i],i==n ? '\n':' ');
	return 0;
}
/*
A,B,aB,a,b,Ab

A
B
aB
baB
bAbaB
bAAbaB
bABAbaB
bABaBAbaB
bABaaBAbaB
bABabaBAbaB
bABabAbaBAbaB
bABabAAbaBAbaB
bABabABAbaBAbaB
bABabABaBAbaBAbaB
bABabABaaBAbaBAbaB
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值