【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 }