【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\n",&t);

74     makepr();

75     while (t--){

76           if (t==0){

77                   t=0;

78           }

79           scanf("%d%d\n",&a,&b);

80           printf("%d\n",work());

81     }

82     fclose(stdin);

83     fclose(stdout);

84 }
View Code

 

你可能感兴趣的:(more)