• [cf1491I]Ruler Of The Zoo


    为了统一描述,下面给出题意——

    有$n$只动物,编号为$i$的动物有属性$a_{i,j}$($0le i<n,0le jle 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且若$jge 3$则游戏结束,否则令$j=1$

    输出游戏结束时的$x$以及过程执行的次数,或游戏永远无法结束

    $4le nle 6 imes 10^{3}$,$0le 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}$,对于$ige 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 }
    View Code
  • 相关阅读:
    《Java程序设计》第五周学习总结
    团队建设任务
    《Java程序设计》第四周学习总结
    ML_Review_GMM(Ch10)
    ML_Review_SVM(Ch9)
    ML_Review_LDA(Ch5)
    ML_Review_PCA(Ch4)
    关于Kernel的思考
    ML_Homework_Porject_2_LDA_KNN
    CV_Learn
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14588589.html
Copyright © 2020-2023  润新知