spoj422 Transposing is Even More Fun

9 篇文章 0 订阅
5 篇文章 0 订阅

http://www.elijahqi.win/archives/3435
题意翻译

现在存储了一个

2^a * 2^b

2a∗2b 的矩阵 • 矩阵在内存中是按行存储的 • 现在你想求它的转置 • 唯一允许的操作是交换两个内存位置的值 • 求最少需要的次数? • 4e5 组询问,每组询问 a + b <= 1e6

感谢@IceFox 提供的翻译

题目描述

输入输出格式

输入格式:

First line of input contains number of test cases c (1<=c<=400000). Each test case consists of two integers a,b (0<=a+b<=1000000).

输出格式:

For each test case output minimal number of swaps required to transpose an 2ax2b array. As it can be quite large, you have to output its remainder when divided by 1000003 (yes, it’s a prime number :).

输入输出样例

输入样例#1: 复制

3
1 1
2 2
5 7
输出样例#1: 复制

1
6
3744
观察到我们把这个内存的地址写下来其实是一些数左移了之后产生的数字

然后观察循环节个数 假如循环节个数是K 那么最后的交换次数就是2^(a+b)-k因为每个循环并不需要交换k次而是k-1次即可都交换完 考虑将原来假设为将a+b个珠子串成项链那么方案数怎么求

首先先看一个例子 假设a=3,b=6手动绘图

可以发现我们旋转之后最多转gcd(a,a+b)下 那么循环节长度即为(a+b)/(gcd(a,b))

考虑我们现在将什么重新标号了 这意味着 我们现在附近的点都会融合成一个点

这样得到一个新的项链

一共(a+b)/gcd(a,b)个珠子每个珠子都是可以用2^gcd(a,b) 填充

然后再接着化简公式即可 用常见的套路将Phi与预处理出来 每次只需要算出之后再*phi[i]&phi[n/i]即可
太困了 证明什么的可以看这位巨佬
https://blog.csdn.net/BraketBN/article/details/50668414

#include<cstdio>
#include<cctype>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int mod=1000003;
const int N=1e6+10;
bool not_prime[N];
int bin[N],phi[N],prime[N],tot,n,g;
inline int gcd(int x,int y){return !y?x:gcd(y,x%y);}
inline int ksm(ll b,int t){static ll tmp;
    for (tmp=1;t;b=b*b%mod,t>>=1) if (t&1) tmp=tmp*b%mod;return tmp;
}
inline void init(){
    phi[1]=1;bin[0]=1;bin[1]=2;
    for (int i=2;i<=1e6;++i){
        bin[i]=bin[i-1]<<1;bin[i]%=mod;
        if (!not_prime[i]) prime[++tot]=i,phi[i]=i-1;
        for (int j=1;prime[j]*i<=1e6;++j){
            not_prime[prime[j]*i]=1;
            if (i%prime[j]==0){
                phi[prime[j]*i]=phi[i]*prime[j];break;
            }else phi[prime[j]*i]=phi[i]*phi[prime[j]];
        }
    }
}
inline int inc(int x,int v){return x+v>=mod?x+v-mod:x+v;}
inline int dec(int x,int v){return x-v<0?x-v+mod:x-v;}
inline int gao(){
    int tmp=0;
    for (int i=1;i*i<=n;++i){
        if (n%i) continue;tmp=inc(tmp,(ll)phi[n/i]*bin[g*i]%mod);
        if(i*i==n) continue;tmp=inc(tmp,(ll)phi[i]*bin[n/i*g]%mod);
    }return (ll)tmp*ksm(n,mod-2)%mod;
}
int main(){
    freopen("spoj422.in","r",stdin);
    int T=read();init();
    while(T--){
        int a=read(),b=read();if (!a||!b) {puts("0");continue;}
        g=gcd(a,a+b);n=(a+b)/g;
        int ans=dec(bin[a+b],gao());printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值