Description

  

  从前有一个策略游戏, 叫做 蚂蚁上树

  游戏中有一棵 nn 个节点, 以 11 为根的有根树

  初始始每个节点都为空, 游戏系统会进行两种操作 :

  1 x , 表示往 xx 节点放入一只睡眠状态中的蚂蚁

  2 x , 表示从 xx 节点取出一只睡眠状态中的蚂蚁

  (对于操作2, 保证取出前该点至少有一只蚂蚁)

  每次操作后, 玩家要进行一轮游戏 :

  游戏有无穷的时间, 每一时刻, 系统会 依次执行 下述五个操作

    1) 让玩家选择 任意多只(可以为 0 只) 睡眠状态中的蚂蚁

    2) 所有亢奋状态的蚂蚁朝根结点方向移动一步

    3) 若某一时刻 ≥2≥2只 亢奋状态 的蚂蚁处在同一节点, 游戏失败

    4) 到达根节点的蚂蚁进入睡眠状态.

    5) 当前时刻被玩家选择的蚂蚁进入亢奋状态

    6) 若所有蚂蚁都在根节点, 游戏结束

  游戏不允许失败, 玩家的游戏目的是 : 使游戏结束时, 最后一只到达根节点的蚂蚁到达时间最早.

  每轮游戏后, 系统会自动将树恢复成玩家该轮游戏前的局面, 然后进行下一次取/放蚂蚁的操作.

  

Input

  

  第一行两个数 n,mn,m 表示树的点数和操作数

  第 2−n2−n 行, 第 ii 行一个数 fifi 表示 ii 节点的父亲

  接下来 mm 行, 每行两个数表示系统的操作

  若为 1 x , 表示往 xx 节点放入一只睡眠状态中的蚂蚁

  若为 2 x , 表示从 xx 节点取出一只睡眠状态中的蚂蚁

  

Output

  

  输出 mm 行, 表示每轮游戏在最优策略下

  最后一只到达根节点的蚂蚁到达的最早时间

  (特别的, 如果所有蚂蚁都在根节点, 或者没有蚂蚁, 输出 0)

  

Sample Input

  

4 5
1
2
2
1 1
1 3
1 4
1 2
2 3

  

Sample Output

  

0
3
4
4
3
``` 
  
## HINT
  
  对于样例输出第四行的解释 :
  第一时刻触碰位于 2, 3 的那只蚂蚁, 他们进入亢奋状态但没有移动
  第二时刻触碰位于 4 的那只蚂蚁, 然后位于 2, 3 的蚂蚁分别爬到 1, 2, 然后爬到 1 的蚂蚁进入睡眠状态, 之后 4 进入亢奋状态.
  第三时刻不触碰蚂蚁, 当前位于 2, 4 的蚂蚁分别爬到 1, 2, 爬到 1 的这只蚂蚁进入睡眠状态
  第四时刻不触碰蚂蚁, 当前位于 2 的蚂蚁爬到 1 并进入睡眠状态, 然后游戏结束
  数据范围 :
  对于 30%的数据, $n,m≤3000$
  对于另外 30% 的数据, $n≤5000$
  对于另外 5% 的数据, 树的最大深度为 2
  对于另外 10% 的数据, 数据的生成方式如下 $fi=rand()%(i−1)+1$
  对于 100% 的数据 :
  $2≤n≤10^5$
  $1≤m≤10^5$
  $1≤fi<i,i=2..n$
  $1≤x≤n$
  
  
  
# Solution
  
  这题看起来很复杂,考场上我想了个反推的模拟,还写挂了,看起来并不对。
  
  考虑每个蚂蚁往上走的过程,是一激活就停不下来的。那么如果两只蚂蚁到达根节点的时间相同,那么它们必定在某一处会相撞。
  
  那么问题等价于为每一只蚂蚁挑一个至少为其深度的到达时间,使得每只蚂蚁的到达时间唯一,且最大时间最小。
  
​   我们应该按深度从小到大考虑每一个深度$i$的$sum_i$只蚂蚁,给同一深度的$sum_i$只蚂蚁挑连续一段的到达时间$[i,i+sum_i)$
  
​   可是我们发现,不同深度挑选的区间一旦有重复,就意味着有蚂蚁会同时刻到达终点,也就是早就会撞上,因此挑选的区间必须互相不重叠。
  
​   那么每个深度的区间开头位置往往取不到$i$,因为前面会顺延下来造成推移。
  
​   答案求的其实是最靠后的一个区间的结束位置,记每个深度为$i$的节点到达时间区间为$[start_i,start_i+sum_i)$,那么实际上$ans=max \{ start_i+sum_i-1\}$。
  
​   然而维护$start$太难了,下面证明实际上$ans=max\{i+a_i-1\},i>0$,其中$a_i$表示深度大于等于$i$的蚂蚁个数。
  
​   首先根节点的蚂蚁全部忽略掉不考虑。
  
​   设深度为$i$的蚂蚁们被分配到的区间是最后的。
  
  ​ 如果开头位置取到了$i$,那么$i+a_i-1$可以贡献到答案,并且发现其他情况时都不可能贡献到比这个还大的位置。
  
​   如果开头位置没有取到$i$,说明这个区间被顺延了,根据贪心策略,这个区间一定紧挨着上一个区间,以此类推形成一个连通块。设连通块最前端的区间是给$j$取的,而且此区间的开始位置一定是最好情况:刚好取到$j$,所以$j+a_j-1$可以贡献到$i$的结尾位置。
  
  ​ 所以用线段树维护一下$i+a_i-1$就好了。
    
```c++
#include <cstdio>
#include <set>
using namespace std;
const int N=100005,M=200005;
namespace IO{
const int SIZE=10000000;
char buffer[SIZE];
int pos;
void init(){fread(buffer,1,SIZE,stdin);}
char getch(){return buffer[pos++];}
int getInt(){
char c=getch(); int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-f;c=getch();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getch();}
return x*f;
}
}
int n,m,antcnt,a[N];
int h[N],tot,dep[N],maxdep;
struct Edge{int v,next;}e[M];
struct Data{
int x;
Data(){}
Data(int _x){x=_x;}
friend bool operator < (const Data &a,const Data &b){
if(dep[a.x]!=dep[b.x])
return dep[a.x]<dep[b.x];
return a.x<b.x;
}
};
set<Data> s;
inline int max(int x,int y){return x>y?x:y;}
inline void addEdge(int u,int v){e[++tot].v=v;e[tot].next=h[u];h[u]=tot;}
void dfs(int u,int fa){
maxdep=max(maxdep,dep[u]);
for(int i=h[u],v;i;i=e[i].next)
if((v=e[i].v)!=fa){
dep[v]=dep[u]+1;
dfs(v,u);
}
}
namespace SEG{/*{{{*/
int rt,sz,ch[N*2][2],maxs[N*2],tag[N*2];
void addtag(int u,int x){
maxs[u]+=x;
tag[u]+=x;
}
inline void pushup(int u){maxs[u]=max(maxs[ch[u][0]],maxs[ch[u][1]]);}
inline void pushdown(int u){
if(tag[u]){
addtag(ch[u][0],tag[u]);
addtag(ch[u][1],tag[u]);
tag[u]=0;
}
}
void build(int &u,int l,int r){
u=++sz;
maxs[u]=r;
if(l==r) return;
int mid=(l+r)>>1;
build(ch[u][0],l,mid);
build(ch[u][1],mid+1,r);
}
void modify(int u,int l,int r,int L,int R,int x){
if(L<=l&&r<=R){
addtag(u,x);
return;
}
pushdown(u);
int mid=(l+r)>>1;
if(L<=mid) modify(ch[u][0],l,mid,L,R,x);
if(mid<R) modify(ch[u][1],mid+1,r,L,R,x);
pushup(u);
}
int query(int u,int l,int r,int L,int R){
if(L<=l&&r<=R) return maxs[u];
pushdown(u);
int mid=(l+r)>>1;
if(R<=mid) return query(ch[u][0],l,mid,L,R);
else if(mid<L) return query(ch[u][1],mid+1,r,L,R);
else return max(query(ch[u][0],l,mid,L,mid),query(ch[u][1],mid+1,r,mid+1,R));
}
}/*}}}*/
int main(){
freopen("input.in","r",stdin);
IO::init();
n=IO::getInt();
m=IO::getInt();
for(int i=2;i<=n;i++){
int x=IO::getInt();
addEdge(x,i);
}
dfs(1,0);
int opt,x;
SEG::build(SEG::rt,0,maxdep);
for(int i=1;i<=m;i++){
opt=IO::getInt();
x=IO::getInt();
if(opt==1){
a[x]++; antcnt++;
if(a[x]==1) s.insert(Data(x));
}
else{
a[x]--; antcnt--;
if(!a[x]) s.erase(s.find(Data(x)));
}
if(x!=1)
SEG::modify(SEG::rt,0,maxdep,0,dep[x],opt==1?1:-1);
if(!antcnt||a[1]==antcnt)
printf("0\n");
else{
set<Data>::iterator pos=s.end();
pos--;
printf("%d\n",SEG::query(SEG::rt,0,maxdep,0,dep[(*pos).x]));
}
}
return 0;
}

​

Ants on tree的更多相关文章

  1. Educational Codeforces Round 7 E. Ants in Leaves 贪心

    E. Ants in Leaves 题目连接: http://www.codeforces.com/contest/622/problem/E Description Tree is a connec ...

  2. 【UVA 1411】 Ants (KM)

    Young naturalist Bill studies ants in school. His ants feed onplant-louses that live on apple trees. ...

  3. UVALive 4043 Ants

    KM   构图求最小权值匹配 保证最小的权值,所连的边一定是能够不相交的. Ants Time Limit: 3000MS   Memory Limit: Unknown   64bit IO For ...

  4. poj 2565 Ants (KM+思维)

    Ants Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 4125   Accepted: 1258   Special Ju ...

  5. [置顶] Ants(Northeastern Europe 2007)

                                                                                      Ants Time Limit: 5 ...

  6. [poj3565]Ants

    [poj3565]Ants 标签(空格分隔):二分图 描述 Young naturalist Bill studies ants in school. His ants feed on plant-l ...

  7. POJ 3565 Ants 【最小权值匹配应用】

    传送门:http://poj.org/problem?id=3565 Ants Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: ...

  8. POJ 3565 Ants(最佳完美匹配)

    Description Young naturalist Bill studies ants in school. His ants feed on plant-louses that live on ...

  9. HDU 4776 Ants(Trie+优先队列)

    Ants Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others) Total S ...

随机推荐

  1. 用Unity的UGUI实现简单摇杆

    1.在Canvas下新建一个空对象作为我们的摇杆,命名为Joystick. 摇杆由背景和杆两部分组成,所以在Joystick下新建一个Image作为摇杆的背景,命名为BG. 在BG下新建一个Image ...

  2. 木马分析出现python语言,360的安全人员不禁感叹还有这种操作?

    几年前,敲诈者木马还是一个默默无闻的木马种类.然而,由于其极强的破坏力和直接且丰厚的财富回报,敲诈者木马这几年已经一跃成为曝光率最高的木马类型——甚至超越了盗号木马.远控木马.网购木马这传统三强.与此 ...

  3. 【坚持】Selenium+Python学习之从读懂代码开始 DAY4

    2018/05/21 [生成器详解:廖雪峰的官方网站](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d ...

  4. vue 组件-父组件传值给子组件

    父组件通过属性,传值给子组件,子组件通过,props数组里的名称来接受父组件传过来的值. HTML部分: <div id="app"> <tmp1 :parent ...

  5. Python科学测量与计算库Pymeasure: 控制你的仪器进行自动测试和科学计算

    Python这种脚本语言因其语法简单,工具包丰富成熟,使用起来非常方便.在很多领域被广泛使用,今天介绍的是python在仪器控制应用领域,python在仪器控制领域相关的书籍可以参考<真实世界的 ...

  6. Redis源码阅读(一)事件机制

    Redis源码阅读(一)事件机制 Redis作为一款NoSQL非关系内存数据库,具有很高的读写性能,且原生支持的数据类型丰富,被广泛的作为缓存.分布式数据库.消息队列等应用.此外Redis还有许多高可 ...

  7. [文章存档]Azure .net WebAPP的js/css文件过大导致访问慢的解决办法

    https://docs.azure.cn/zh-cn/articles/azure-operations-guide/app-service-web/aog-app-service-web-qa-j ...

  8. python3 拼接字符串的7种方法

    1.直接通过(+)操作符拼接 1 2 >>> 'Hello' + ' ' + 'World' + '!' 'Hello World!' 使用这种方式进行字符串连接的操作效率低下,因为 ...

  9. kali linux执行apt-get update失败(数字签名过期)

    想要安装某个软件,执行apt-get update 失败,出现下面的错误: 自己查看了更新源是没有问题的,根据提示的错误google了一下,发现是数字签名过期了. 执行下面命令: apt-key ad ...

  10. Python基础_内置函数

        Built-in Functions     abs() delattr() hash() memoryview() set() all() dict() help() min() setat ...