[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 }
[cf1491I]Ruler Of The Zoo的更多相关文章
- zookeeper的zoo.cfg的配置
zookeeper的默认配置文件为zookeeper/conf/zoo_sample.cfg,需要将其修改为zoo.cfg.其中各配置项的含义,解释如下: tickTime:CS通信心跳时间 Zook ...
- (转)The Neural Network Zoo
转自:http://www.asimovinstitute.org/neural-network-zoo/ THE NEURAL NETWORK ZOO POSTED ON SEPTEMBER 14, ...
- Mininet建立topology zoo中的拓扑
以前用Mininet建立拓扑都是在别人的代码上进行需求上的修改,这次从头开始将topology zoo(http://www.topology-zoo.org/)中的拓扑用Mininet建立,不失一般 ...
- CSS Ruler 前端工具
CSS Ruler是一款在线的CSS单位工具. CSS Ruler 彩蛋爆料直击现场 http://katydecorah.com/css-ruler/
- zoo.cfg配置
zookeeper的默认配置文件为zookeeper/conf/zoo_sample.cfg,需要将其修改为zoo.cfg.其中各配置项的含义,解释如下: 1.tickTime:CS通信心跳时间 Zo ...
- Codeforces Round #250 (Div. 1) B. The Child and Zoo 并查集
B. The Child and Zoo Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/438/ ...
- cf437D The Child and Zoo
D. The Child and Zoo time limit per test 2 seconds memory limit per test 256 megabytes input standar ...
- CF437D(The Child and Zoo)最小生成树
题目: D. The Child and Zoo time limit per test 2 seconds memory limit per test 256 megabytes input sta ...
- Codeforces 437D The Child and Zoo(贪心+并查集)
题目链接:Codeforces 437D The Child and Zoo 题目大意:小孩子去參观动物园,动物园分非常多个区,每一个区有若干种动物,拥有的动物种数作为该区的权值.然后有m条路,每条路 ...
随机推荐
- c++ fstream feekg讨论
#include <iostream> #include <fstream> using namespace std; int main() { std::ifstream f ...
- ArcToolbox工具箱
3D Analyst 工具 Data Interoperability Tools Geostatistical Analyst 工具 Network Analyst 工具 Schematics 工具 ...
- NOIP模拟76
前言 还有不到 10 天就要 CSP-S ...马上我就要有我的第一篇游记了. 今天考试莽了一回,整了大概 2.5h 的 T1 ,可能是因为今天题目比较难,看起来成效不错. 以后还是要注意时间的分配( ...
- CF613D Kingdom and its Cities(虚树+贪心)
很休闲的一个题啊 其实一看到关于\(\sum k\)的限制,就知道是个虚树的题了 首先我们把虚树建出来,然后考虑怎么计算个数呢? 我们令\(f[x]\)表示以\(x\)的子树中,剩余了多少个还没有切断 ...
- Salesforce 生命周期管理(一)应用生命周期浅谈
本篇参考: https://trailhead.salesforce.com/en/content/learn/trails/determine-which-application-lifecycle ...
- mysql group by语句流程是怎么样的
group by流程是怎么样的 注意点: select id%10 as m, count(*) as c from t1 group by m; group by是用于对数据进行分组,我们排序用到了 ...
- python中单引号、双引号和三引号
在python中字符串可以用双引号表示,也可以用单引号表示: str1 = 'hello world'str2 = "hello world" 这两种字符串的表示方法没有区别. p ...
- Kali Linux修改root密码
今天在官网下载了一个Kali虚拟机的压缩包, 解压缩后直接在VM中打开,点了好多次打开发现都打不开,查了一下说是没有关闭共享虚拟机,于是操作了一番: 编辑→首选项→共享虚拟机→选择禁用. 操作完之后果 ...
- 按键检测GPIO输入
1. 项目 通过按键控制开关LED灯,按下按键灯亮,再按一下灯灭. 2. 代码 mian.c #include "stm32f10x.h" //相当于51单片机中的 #includ ...
- Linux多线程编程实例解析
Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...