【SPOJ】Transposing is even more fun!

题意:

给出a、b 表示按先行后列的方式储存矩阵 现在要将其转置 可以交换两个点的位置 求最小操作次数

题解:

储存可以将其视为拉成一条链 设a=5、b=2 则在链上坐标用2^***(a,b)表示为(xxxxxyy) 转置后为(yyxxxxx)

这时将其视为另一个点的坐标 继续转置为(xxyyxxx)... 直到再变成(xxxxxyy)这样每次循环可以节省1次转置 所以ans=2^(a+b)-k k为循环的个数
k的计算:右移b位 右移b*2位 右移b*3位... 构成了一个置换群 置换个数为(a+b)/***(a,b)

因为它是循环的 所以向右移bx位 可视为右移bx%(a+b)位 设bx=z(mod (a+b))

该方程有解条件为***(b,(a+b))|z -> ***(a,b)|z 所以z为***(a,b)的倍数

k的值可理解为将长度为(a+b)/***(a,b)的串 染成2^***(a,b)种颜色(循环移动视为同种方案) 的方案数

既为poj2154的题目


因为spoj会卡常数 这题很容易TLE 我做了几个优化:

1.记忆化欧拉函数 将算过的欧拉函数存下来 下次直接用

2.预处理幂 可以发现这题要用的幂都是2^x 可以直接预处理出来

3.dfs n的因数 枚举m的因数会浪费很多时间 可以先算出n的质因数 在通过dfs算出其因数

4.尽量不要用long long 在会超int的地方强制转换一下就行

这题时限是8s 我跑了4.2s 目测前面T了十几次

代码:

 1 #include <cstdio>
 2 #define _(x) static_cast<long long>(x)
 3 const int mo=1000003;
 4 typedef int ll;
 5 ll t,a,b,n,m,g,phii[1000001],primer[1001],np,flag[1001],pow[1000001],ans,p[1001],nn;
 6 void makep(ll t){
 7      for (int i=1;i<=np && t>1 && primer[i]*primer[i]<=t;i++)
 8      if (t%primer[i]==0){
 9                          p[++nn]=primer[i];
10                          while (t%primer[i]==0) t/=primer[i];
11      }
12      if (t>1) p[++nn]=t;
13 }
14 ll phi(ll t){
15           ll out=1,tt=t;
16           if (phii[t]) return phii[t];
17           for (int i=1;i<=np && t>1 && primer[i]*primer[i]<=t;i++)
18           if (t%primer[i]==0){
19                               t/=primer[i];
20                               out=_(out)*_((primer[i]-1))%mo;
21                               while (t%primer[i]==0) t/=primer[i],out=_(out)*_(primer[i])%mo;
22           }
23           if (t>1) out=_(out)*_((t-1))%mo;
24           return phii[tt]=out;
25 }
26 void dfs(ll now,ll sum){
27      if (now>nn){
28                  ans=(ans+_(phi(n/sum))*_(pow[sum*g]))%mo;
29                  return;
30      }
31      for (ll i=1;n%i==0;i*=p[now]) dfs(now+1,sum*i);
32 }
33 void extgcd(ll a,ll b,ll &x,ll &y){
34      if (!b) x=1,y=0;
35      else{
36           extgcd(b,a%b,x,y);
37           ll t=x;
38           x=y;
39           y=t-a/b*y;
40      }
41 }
42 ll gcd(ll a,ll b){
43           while (b) b^=a^=b^=a%=b;
44           return a;
45 }
46 ll work(){
47           if (!a || !b) return 0;
48           ll x,y;
49           g=gcd(a,b);
50           n=(a+b)/g;
51           nn=ans=0;
52           makep(n);
53           dfs(1,1);
54           extgcd(n,mo,x,y);
55           x=(x%mo+mo)%mo;
56           ans=(_(ans)*_(x))%mo;
57           return ((pow[a+b]-ans)%mo+mo)%mo;
58 }
59 void makepr(){
60      for (int i=2;i<=1000;i++){
61          if (!flag[i]) primer[++np]=i;
62          for (int j=1;j<=np && primer[j]*i<=1000;j++){
63              flag[primer[j]*i]=1;
64              if (i%primer[j]==0) break;
65          }
66      }
67      pow[0]=1;
68      for (int i=1;i<=1000000;i++) pow[i]=(pow[i-1]*2)%mo;
69 }
70 int main(){
71     freopen("spoj442.in","r",stdin);
72     freopen("spoj442.out","w",stdout);
73     scanf("%d
",&t);
74     makepr();
75     while (t--){
76           if (t==0){
77                   t=0;
78           }
79           scanf("%d%d
",&a,&b);
80           printf("%d
",work());
81     }
82     fclose(stdin);
83     fclose(stdout);
84 }
View Code
原文地址:https://www.cnblogs.com/g-word/p/3691759.html