[cf1491I]Ruler Of The Zoo
为了统一描述,下面给出题意——
有$n$只动物,编号为$i$的动物有属性$a_{i,j}$($0\le i<n,0\le j\le 2$)
初始$n$只动物从左到右编号依次为$0,1,...,n-1$,重复以下过程:
(初始$j=0$,假设最左边的两只动物编号依次为$x$和$y$)
1.比较$a_{x,j}$和$a_{y,0}$,将其中较小的那只动物移动到最右边
2.若比较中$a_{x,j}>a_{y,0}$,则令$j$增加1且若$j\ge 3$则游戏结束,否则令$j=1$
输出游戏结束时的$x$以及过程执行的次数,或游戏永远无法结束
$4\le n\le 6\times 10^{3}$,$0\le a_{i,j}\le 10^{9}$,$a_{i,1}<\min(a_{i,0},a_{i,2})$且保证$a_{i,j}$各不相同
以下,我们将题解分为三部分,可能与原题解略有不同
第一部分:颜色的定义即变化
为了方便,我们先手动模拟第一次(即比较$a_{0,0}$和$a_{1,0}$)
对于某一个时刻,我们假设所有动物从左到右编号依次为$id_{0},id_{1},...,id_{n-1}$,给每一只动物一个颜色(红色或非红色),其中$id_{i}$为红色当且仅当$a_{id_{i},0}<a_{id_{i-1},1}$
(特别的,$id_{0}$为“红色”当且仅当$a_{id_{0},0}<a_{id_{n-1},1}$)
首先,考虑$id_{0}$一定非红(手动模拟一次后),证明如下:
考虑$id_{0}$和$d_{n-1}$也就是上一次比较的两只动物且$id_{0}$较大,而若其为红色即$a_{id_{0},0}<a_{id_{n-1},1}$,那么唯一有可能比$id_{n-1}$大的仅有$a_{id_{0},2}$,即比较时$j=2$且胜利后变为$j=3$,游戏已经结束
接下来需要分析每一次颜色的变化,显然将$x$移动到最右边是不会导致动物颜色变化的,只有当$a_{x,j}>a_{y,0}$,也就是$y$移动到最右边时会变化,此时对$j$分类讨论:
1.当$j=1$时,由于$a_{x,1}>a_{y,0}$,根据定义也就是$y$为红色,再令$y'$为$y$下一个位置(即$id_{2}$),颜色会发生变化的也就是$x$、$y$和$y'$这三个位置
对于$x$,其本来是非红色,且$y$为红色可得$a_{y,0}<a_{x,1}$,再根据$a_{i,1}<a_{i,0}$即可得$a_{y,1}<a_{x,0}$,也就是说$x$仍然是非红色
对于$y$,其本来是红色,但根据颜色的信息无法确定其最终的颜色
对于$y'$,对其初始颜色分类讨论:
(1)若其为红色,即$a_{y',0}<a_{y,1}$,不难得到$a_{y',0}<a_{x,1}$,即仍然是红色
(2)若其为非红色,即$a_{y',0}>a_{y,1}$,同样根据颜色的信息无法确定其最终的颜色
2.当$j=2$时,此时若$a_{x,j}>a_{y,0}$即$x$获得胜利,也就没有颜色变化了
综上分析,颜色变化仅在$j=1$且$a_{x,j}>a_{y,0}$($y$移动到最右边)时红色的$y$变为非红色,或非红色的$y'$变为红色(这些变化有可能不发生,但发生的一定是这些变化)
但对于第2种$y'$变为红色,对于下一次,其是红色即$a_{y',0}<a_{x,1}$,同时$a_{x,1}<a_{x,2}$,那么$x$就胜利了
因此,至多只有一次非红色变为红色,根据红色的数量总在$[0,n]$之间,红色变为非红色的次数也至多只有$n+1$次,另外还有至多1次结束操作
对于使得颜色变化或结束的操作,称作特殊操作,数量为$o(n)$
第二部分:操作分组
下面考虑将$n-1$次操作合并为一组操作(手动模拟的第一次操作不计入其中,即$1+(n-1)+(n-1)+...$的形式),显然包含特殊操作的组数也是$o(n)$的
注意到一组操作中,如果初始状态是$id_{i}$,那么第$i$次操作的$y$即为$id_{i}$,且最终放到最后的也就作为下一次的$id\ '_{i}$,最后一轮中未放到最后的是$id\ '_{0}$
如果已经确定某一组内不包含特殊操作,考虑这一组操作的效果——
首先有以下性质:在这一组中的操作,$a_{x,j}>a_{y,0}$当且仅当$y$为红色
$j=1$根据定义是显然的;$j=2$则由于没有结束操作,比较结果必然是$a_{x,2}<a_{y,0}$,也即可推出$a_{x,1}<a_{y,0}$,那么$y$即为非红色,符合性质
且由于没有发生使颜色变化操作,这个$y$的颜色即初始状态中的颜色
由此归纳可得:第$i$次操作时$x$为$[0,i)$之间最后一个非红色的$id_{j}$($id_{0}$为非红色总是存在)且$y=id_{i}$
下面来考虑新的编号序列$id\ '_{i}$,对于$i\ge 1$的$id\ '_{i}$,对第$i$次的$y=id_{i}$分类讨论来确定其值:
1.若$y$为红色,也就是$id\ '_{i}=id_{i}$
2.若$id_{i}$为非红色,那么$id\ '_{i}=x$,也就是其之前第一个非红色的位置
(特别的,$id\ '_{0}$是最后一次操作中未放到最后的,类似归纳过程可得即最后一个非红色的$id_{j}$)
总得来说,最后的$id\ '_{i}$就是在$id_{i}$的基础上,红色位置的不变,非红色的位置向右旋转一圈,之后$j$的值是取决于最后一个位置,即若是红色$j=2$,否则$j=1$
第三部分:快速找到下一个包含特殊操作的组
下面,如果能对于一个$id_{i}$,我们需要知道执行多少组操作(旋转多少次)后可以使得下一组操作中包含特殊操作,再暴力执行这些操作(旋转和下一组)即可
旋转和暴力执行一组操作都是可以做到$o(n)$的,那么关键就是如何$o(n)$找到这个次数
事实上,去除一些细节问题后,比较复杂的主要有两点:
1.对于每一个红色的位置$id_{i}$,找到旋转最少的次数使得其上两个的红色位置$id_{j}$满足$a_{id_{j},1}<a_{id_{i},0}$
2.对于所有相邻的非红色的位置$id_{i}$和$id_{j}$(并不一定有$|i-j|=1$,允许中间有红色),满足$a_{id_{i},2}>a_{id_{j},0}$找到其旋转的最少次数使得其恰好夹着一个红色位置
对于第一点,可以维护一个单调栈,将非红色的位置$a_{id_{j},1}$的后缀最小值找出(很明显既靠后又小的一定优),之后当有红色位置,将其从左边不断弹出比当前位置小的即可
之后由于旋转是环,将序列重复两次即可,另外旋转是非红色的位置,需要维护非红色位置的前缀和
对于第二点,将所有非红色位置提出后,从前往后记录最后一个满足此性质的,当遍历到红色(两个非红色之间有间隙),计算最后一个移动到这个红色的距离即可
略微补充一下细节问题:
1.如果出现相邻的红色,可以直接判定这一轮一定结束;
2.需要先执行若干次,来保证$j$与最后一个位置颜色相同(只执行一次并不一定足够,因为可能有连续两次都包含特殊操作)
总复杂度即$o(n^{2})$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 6005 4 vector<int>v; 5 deque<int>q; 6 int n,id[N],idd[N],tot[N<<1],a[N][3]; 7 long long sum; 8 int red(int k){ 9 if (!k)return 0; 10 return a[id[k]][0]<a[id[k-1]][1]; 11 } 12 int find(){ 13 int j=red(n-1)+1,ans=0x3f3f3f3f; 14 if ((j==2)&&(red(1)))return 0; 15 for(int i=1;i<n;i++) 16 if ((red(i-1))&&(red(i)))return 0; 17 tot[0]=1; 18 for(int i=1;i<2*n;i++)tot[i]=tot[i-1]+(!red(i%n)); 19 q.clear(); 20 for(int i=0;i<2*n;i++) 21 if (red(i%n)){ 22 while ((!q.empty())&&(a[id[q.front()%n]][1]<a[id[i%n]][0])){ 23 ans=min(ans,tot[i]-tot[q.front()]-1); 24 q.pop_front(); 25 } 26 } 27 else{ 28 while ((!q.empty())&&(a[id[q.back()%n]][1]>a[id[i%n]][1]))q.pop_back(); 29 q.push_back(i); 30 } 31 v.clear(); 32 for(int i=0;i<n;i++) 33 if (!red(i))v.push_back(i); 34 if ((j==2)&&(a[id[v[0]]][2]>a[id[v[1]]][0]))return 0; 35 int lst=-1; 36 if (a[id[v.back()]][2]>a[id[v[0]]][0]){ 37 lst=0; 38 if (j==2)ans=min(ans,1); 39 } 40 for(int i=1;i<v.size();i++){ 41 if (a[id[v[i-1]]][2]>a[id[v[i]]][0]){ 42 if (v[i-1]+1<v[i])return 0; 43 lst=v[i]; 44 } 45 if ((lst>=0)&&(v[i-1]+1<v[i]))ans=min(ans,v[i]-lst-1); 46 } 47 if (lst>=0){ 48 if (j==2)ans=min(ans,n-lst); 49 for(int i=1;i<v.size();i++) 50 if (v[i-1]+1<v[i])ans=min(ans,v[i]+(n-lst)-1); 51 } 52 if (j==2){ 53 if (a[id[v[0]]][0]<a[id[v.back()]][1])return 0; 54 for(int i=1;i<v.size();i++) 55 if (a[id[v[i]]][0]<a[id[v[i-1]]][1])ans=min(ans,n-v[i]); 56 } 57 if (ans>n)return -1; 58 return ans; 59 } 60 void turn(int k){ 61 sum+=k*(n-1); 62 v.clear(); 63 for(int i=0;i<n;i++){ 64 if (red(i))idd[i]=id[i]; 65 else v.push_back(i); 66 } 67 for(int i=0;i<v.size();i++)idd[v[i]]=id[v[(i+v.size()-k)%v.size()]]; 68 memcpy(id,idd,sizeof(id)); 69 } 70 int calc(int j){ 71 int lst=id[0]; 72 for(int i=1;i<n;i++){ 73 sum++; 74 if (a[lst][j]>a[id[i]][0]){ 75 idd[i]=id[i]; 76 if (++j>=3){ 77 printf("%d %lld",lst,sum); 78 exit(0); 79 } 80 } 81 else{ 82 idd[i]=lst; 83 lst=id[i]; 84 j=1; 85 } 86 } 87 idd[0]=lst; 88 memcpy(id,idd,sizeof(id)); 89 return j; 90 } 91 int main(){ 92 scanf("%d",&n); 93 for(int i=0;i<n;i++) 94 for(int j=0;j<3;j++)scanf("%d",&a[i][j]); 95 for(int i=1;i<n;i++)id[i]=i+1; 96 sum=1; 97 if (a[0][0]<a[1][0]){ 98 id[0]=1; 99 id[n-1]=0; 100 } 101 else{ 102 id[n-1]=1; 103 id[0]=0; 104 } 105 int j=1; 106 while (1){ 107 while (j!=red(n-1)+1)j=calc(j); 108 int i=find(); 109 if (i<0){ 110 printf("-1 -1"); 111 return 0; 112 } 113 turn(i); 114 j=calc(j); 115 } 116 }