题解 P2250 【二面体群】

· · 题解

我们先来审一下题

角度为 $\frac{360k}{n}$ 度 那也就是说一个**圆被 n 个点均分为 n 个单位**,标号为 $0$ 的点在 $x$ 的正半轴上,其他点按逆时针顺序有序排列。 需要进行两种操作: 'r':顺时针旋转 $\frac{360}{n}$,也就是每个点顺时针转动一个单位。对于'r' $a$,转动次数 $a$ 如果大于 $n$ 那么就相当于转了**超过一圈**了,那么就**直接取模**,相当于转了 $a\%n$ 个单位。 'm':相对于 x 轴对称。对于 'm' $a$,对称次数 $a$ 如果大于 $1$,容易知道**对称两次会自我抵消**,只有 $a$ **是奇数**时,所有点才需要关于 x 轴对称。 继续想可以想到两点: ①给出的点的序列是有序的,在变换过程中**始终有序**。如果只需要支持旋转操作,那么仅需要记录出来某个点作为**基准点**,就可以**代表一个序列**。 ②更进一步,如果要支持对称操作,我们发现,可以将对称后的序列改为**逆时针序列**,对称前的叫**顺时针序列**,**顺时针序列顺时针旋转**,**逆时针序列逆时针旋转**,我们甚至不需要更改基准点的位置,只需要记录是顺时针序列还是逆时针序列即可。 ![逆时针序列变换图示](https://s3.bmp.ovh/imgs/2023/03/20/e08d278e88ff7e47.png) 对于得出**最短操作序列**: 如果是**逆时针序列**: ①一定需要在对准基准点之后确保对称了奇数次; ②如果基准点逆时针操作数少可以先对称,后旋转。 如果是**顺时针序列**: ①如果基准点逆时针操作数少可以借助对称两次,得到最短序列; ②对称两次的操作序列长度需要加 $2$。 ## 思路总结: $p$ 表示基准点位置,$f=1$ 表示顺时针序列,$f=0$ 表示逆时针序列。 顺时针序列旋转 $a$ 次,则 $p=(p+a\%n)\%n$; 逆时针序列旋转 $a$ 次,则 $p=(n+p-a\%n)\%n$; 对称操作 $a$ 次,若 $a$ 为奇数,只需要更改 $f$ 即可。 #### 图示: 例如以下数据: ~~~~ 10 r13 m3 r5 ~~~~ 注:$p$ 表示基准点的位置,其中位置是**顺时针描述**的。其中的“①②...”表示**点的标号**,与本题解的做法没有太大关系。 ![所给数据的图示](https://s3.bmp.ovh/imgs/2023/03/20/63b4ff60aed56520.gif) **模拟**一遍得到**最终状态**。 最后我们需要考虑得到**最终状态**的**最短操作序列**。 #### 边角情况的讨论: $3\leqslant N \leqslant 10^8$,就是说 $N\not=2$,不存在既可以是逆时针序列又是顺时针序列的情况。 考虑这组数据: ~~~~ 60 r31 ~~~~ 最后结果可以是: ~~~~ m1 r29 m1 ~~~~ 也可以是 ~~~~ r31 ~~~~ 但是本题没有 $\color{blue} Special Judge$,也确实没有这种数据,可以放心做本题。还有一种数据与此类似,但都不必讨论。 ## 代码: ```cpp #include<cstdio> int read() { int a(0);char c(getchar()); while(c<'0')c=getchar(); while(c>='0')a=a*10+(c^48),c=getchar(); return a; } int main() { int n(read()),a,p(0),f(1); //p初值为0,表示选取基准点为0号点,f=1,f=0分别表示顺时针序列、逆时针序列 for(char c=getchar();~c;c=getchar())//~c表示如果读入-1(EOF)跳出 { if(c<33)continue; //ASCII中前32个字符中不包含数字和字母,直接吃掉继续读入 a=read(); if(c=='r') { a%=n; if(f)p=(p+a)%n; else p=(n+p-a)%n;//p有可能为负,需要+n } else if(a&1)f=!f; } if(p==0) { if(!f)return printf("m1"),0; else return 0; }//如果没有旋转操作直接结束程序,最多对称一下 if(f) { //顺时针序列: //n-p+2表示借助两次对称进行旋转,p表示直接旋转,可以取等,不必讨论这种情况 if(n-p+2<p)printf("m1 r%d m1",n-p); else printf("r%d",p); } else { //逆时针序列: //n-p表示先对称后旋转,p表示先旋转后对称,可以取等,不必讨论这种情况 if(n-p<p)printf("m1 r%d",n-p); else printf("r%d m1",p); } } ```