[主席树]ZOJ3888 Twelves Monkeys

题意:有n年,其中m年可以乘时光机回到过去,q个询问

下面m行,x,y 表示可以在y年穿越回x年, 保证y>x

下面q个询问, 每个询问有个年份k

问的是k年前面 有多少年可以通过一种以上($\ge 2$)方法穿越回去的, 其中时光机只能用一次

比如案例

9 3 3
9 1
6 1
4 1
6
7
2

如图

对于询问

6这一年:1.穿越回第1年

      2.等时间过呀过呀过到第9年,再穿越回第1年

那么第1年就有两种方法可以穿越回去, 同理, 2、3、4年也有同样两种方法(回到1再等时间过呀过 过到2、3、4)

所以对于6, 有一种以上方法回去的年有1、2、3、4、5总共五年, 输出5

一开始觉得可以用区间覆盖  覆盖小于两次的就为0, 大于等于2的用rmq找左端点

后来发现这样并不可行... ... 因为时光机只能用一次, 那么对于类似这种的  5这个点就无法处理啦

那么换一种想法

既然要一种以上穿越回去的办法(时间并不会倒流)那么就 要求 该时间点后面至少有两个时间点可以穿越回去 否则答案就为0

而穿越回去的  离自己最第二远的  就是自己能穿越回的超过一种方法的最远的地方

来看这个案例

  对于6这个时间,它能穿越回1、2、3、4, 从1可以等到2、3、4

因此第二小的时间点就是能回去的最远的时间点(超法1种方法)

那么问题就转化成了如何求区间第二大的问题

那这不就是主席树的功能了嘛!

好的,接下来只要把m个穿越的方法当作m个区间处理就好了

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long LL;
  4 typedef pair<int, int> PI;
  5 #define lson l, m
  6 #define rson m+1, r
  7 const int N=50005;
  8 int L[N<<5], R[N<<5], sum[N<<5];
  9 int tot;
 10 int T[N], Hash[N];
 11 int build(int l, int r)
 12 {
 13     int rt=(++tot);
 14     sum[rt]=0;
 15     if(l<r)
 16     {
 17         int m=(l+r)>>1;
 18         L[rt]=build(lson);
 19         R[rt]=build(rson);
 20     }
 21     return rt;
 22 }
 23
 24 int update(int pre, int l, int r, int x)
 25 {
 26     int rt=(++tot);
 27     L[rt]=L[pre], R[rt]=R[pre], sum[rt]=sum[pre]+1;
 28     if(l<r)
 29     {
 30         int m=(l+r)>>1;
 31         if(x<=m)
 32             L[rt]=update(L[pre], lson, x);
 33         else
 34             R[rt]=update(R[pre], rson, x);
 35     }
 36     return rt;
 37 }
 38
 39 int query(int u, int v, int l, int r, int k)
 40 {
 41     if(l>=r)
 42         return l;
 43     int m=(l+r)>>1;
 44     int num=sum[L[v]]-sum[L[u]];
 45     if(num>=k)
 46         return query(L[u], L[v], lson, k);
 47     else
 48         return query(R[u], R[v], rson, k-num);
 49 }
 50
 51 PI a[N];
 52
 53 int main()
 54 {
 55     int n, m, q;
 56     while(~scanf("%d%d%d", &n, &m, &q))
 57     {
 58         tot=0;
 59         for(int i=1; i<=m; i++)
 60         {
 61             int x, y;
 62             scanf("%d%d", &x, &y);
 63             a[i]=make_pair(x, y);
 64         }
 65         sort(a+1, a+1+m);
 66         for(int i=1;i<=m;i++)
 67             Hash[i]=a[i].second;
 68         int d=unique(Hash+1, Hash+1+m)-Hash;
 69         T[0]=build(1, d);
 70         for(int i=1; i<=m; i++)
 71         {
 72             int x=lower_bound(Hash+1, Hash+1+m, a[i].second)-Hash;
 73             T[i]=update(T[i-1], 1, d, x);
 74         }
 75         while(q--)
 76         {
 77             int k;
 78             scanf("%d", &k);
 79             int p=lower_bound(a+1, a+1+m, make_pair(k, 0))-a;
 80             if(m-p<1)
 81             {
 82                 puts("0");
 83                 continue;
 84             }
 85             int x=query(T[p-1], T[m], 1, d, 2);
 86             int ans=k-Hash[x];
 87             if(ans<=0)
 88                 puts("0");
 89             else
 90                 printf("%d\n", ans);
 91         }
 92     }
 93     return 0;
 94 }
 95 /*
 96 9 3 9
 97 9 7
 98 8 5
 99 6 3
100 1 2 3 4 5 6 7 8 9
101 */

ZOJ 3888

时间: 2024-08-28 13:09:07

[主席树]ZOJ3888 Twelves Monkeys的相关文章

zoj 3888 Twelves Monkeys 二分+线段树维护次小值

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3888 Twelves Monkeys Time Limit: 5 Seconds      Memory Limit: 32768 KB James Cole is a convicted criminal living beneath a post-apocalyptic Philadelphia. Many years ago, the Earth's surf

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

BZOJ_3207_花神的嘲讽计划1_(Hash+主席树)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3207 给出一个长度为\(n\)的串,以及\(m\)个长度为\(k\)的串,求每个长度为\(k\)的串在原串\([x,y]\)区间是否出现过. 分析 这道题要求对比长度为\(k\)的串,于是我们把这些串的Hash值都算出来,问题就转化成了求\([x,y]\)的区间中是否出现过某Hash值. 求区间中某一个值出现了多少次,可以用主席树. p.s. 1.学习了主席树指针的写法,比数组慢好多啊...

POJ2104主席树模板题

完成新成就——B站上看了算法https://www.bilibili.com/video/av4619406/?from=search&seid=17909472848554781180#page=2 K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 60158   Accepted: 21054 Case Time Limit: 2000MS Description You are working

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

主席树

#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <cstdlib> #define maxn 100005 using namespace std; int b[maxn],t[maxn],n,m,size,v[maxn],tot

(LCA+树上主席树)FZU 2237 - 中位数

题意: 多次查询一个树链上的中位数(其实就是求K大). 分析: 感觉莫队可做,只是不会树上莫队.. 而且这里是边权,处理起来貌似有点小麻烦.. 后来发现其实貌似是一个很老的题,,kuangbin模板书上有类似的题. 树链上的第K大数,这是一道可以用主席树解的题,复杂度才nlogn. 这里也是这样先求从根到每个点的线段树,以为树存在父子关系,所有可以从让下层继承上层的线段树,非常符合主席树的可持久化思想. 然后在查询第K大的时候,去掉重复部分,就可以查了. 太强了,,, 代码: 1 #includ