[Nowcoder113E]弹球弹弹弹_线段树
弹球弹弹弹
题目大意:有n个位置,标号为1到n的整数,m次操作,第i次操作放置一个弹球在b[i] xor c[i-1]处,并询问b[i] xor c[i-1]处弹球个数c[i]每次操作后,在x处的弹球被弹到a[x],规定c[0]=0。
数据范围:1<=n,m<=500000。
题解:
这个题真的是,说起来容易写起来是真的恶心.....
首先要读题,每次操作是这样的:
先加进来一个球,然后查询当前位置的个数,接着直接把每个位置弹到对应位置。
显然,每个点有一条出边,这是一个基环内向森林的模型。
我们把询问分成两种情况讨论:第一种是查询不在环上的点,第二种是环上的点。
第一种怎么弄呢?
首先发现,如果一个球进了环就出不来了,所以我们只需要统计这个点子树内的情况(我们以基环树的环为根,每个数的根就是对应环上的点)。
发现,如果一个点子树内的点$u$满足加入树的时间$time_u$加上和当前点的距离$dep[u]-dep[now]$等于当前时间$nowtime$的话,这个点就可以被统计到。
故此把每个点的权值设为$time_x+dep_x$,只需要查询当前点的子树中,权值等于$nowtime+dep[x]$的点个数。
这个可以用线段树啊平衡树啊什么的实现,我这里用的是线段树。
具体地,我们对于每个权值开一棵线段树,最多需要开$maxtime + maxdep$棵。
线段树上每个节点维护其管辖区间里,所有权值等于当前线段树权值的点个数。
修改就直接在对应权值里修改就好了。
好,第一种情况我们就处理完了,复杂度是$O(nlogn)$。
看第二种情况。
第二种情况的答案贡献有两个渠道,第一个是本身修改就在环上修改的点,第二个是从树上走到环里的点。
第一个好弄,我们对每个环建立一个循环数组,环上每个点有个偏移量$skew_x$表示其距离环上拟定$0$号点的距离。
每次修改我们都直接修改到对应的节点上,就是修改到一次都没弹过的节点上,查询也上那儿查。
第一个就弄完了。
看看第二个:
第二个的话,就是修改是在树上修改,但是走到环上了。
怎么办呢?我们就弄一个$vector$叫$Fix_time$。
$Fix_i$表示所有在第$i$时刻的点集合。
对于一个在树上修改的点,我们把它扔进$Fix_{dep[x]+nowtime}$。
当处理完修改和查询之后,我们处理当前时刻的$Fix$数组。
每次只需要看一下,对应的点需要加到没有弹过的哪个环上节点就好了。
难写难调。
还有,这个题好像平衡树卡常,线段树秒过。
我写的线段树
代码:
这个是删掉所有调试注释的,方便取用。
- #include <bits/stdc++.h>
- #define N 500010
- using namespace std;
- struct Edge {
- int to[N << 1], nxt[N << 1], tot, head[N];
- inline void add(int x, int y) {
- to[ ++ tot] = y;
- nxt[tot] = head[x];
- head[x] = tot;
- }
- }G1,G2;
- int d[N], dep[N], qu[N], dfn[N], Q[N], vis[N], sk[N], sz[N], top[N];
- int root[N << 1];
- bool disp[N];
- queue<int> q;
- vector<int> Round[N], Ans[N];
- int cnt;
- vector<int> Fix[N << 1];
- struct Segment_Tree {
- int ls, rs, sum;
- }a[N << 8];
- char *p1, *p2, buf[100000];
- #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
- int rd() {
- int x = 0, f = 1;
- char c = nc();
- while (c < 48) {
- if (c == '-')
- f = -1;
- c = nc();
- }
- while (c > 47) {
- x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
- }
- return x * f;
- }
- int n;
- void toposort() {
- for (int i = 1; i <= n; i ++ ) {
- if (!d[i]) {
- vis[i] = -1;
- q.push(i);
- }
- }
- while (!q.empty()) {
- int x = q.front();
- q.pop();
- for (int i = G1.head[x]; i; i = G1.nxt[i]) {
- d[G1.to[i]] -- ;
- if (!d[G1.to[i]]) {
- q.push(G1.to[i]);
- vis[G1.to[i]] = -1;
- }
- }
- }
- }
- inline void pushup(int x) {
- int ls = a[x].ls, rs = a[x].rs;
- a[x].sum = 0;
- if (ls) {
- a[x].sum += a[ls].sum;
- }
- if (rs) {
- a[x].sum += a[rs].sum;
- }
- }
- void update(int x, int val, int l, int r, int &p) {
- if (!p) {
- p = ++cnt;
- }
- if (l == r) {
- a[p].sum ++ ;
- return;
- }
- int mid = (l + r) >> 1;
- if (x <= mid) {
- update(x, val, l, mid, a[p].ls);
- }
- else {
- update(x, val, mid + 1, r, a[p].rs);
- }
- pushup(p);
- }
- int query(int x, int y, int l, int r, int p) {
- if (!p) {
- return 0;
- }
- if (x <= l && r <= y) {
- return a[p].sum;
- }
- int mid = (l + r) >> 1, ans = 0;
- if (x <= mid) {
- ans += query(x, y, l, mid, a[p].ls);
- }
- if (mid < y) {
- ans += query(x, y, mid + 1, r, a[p].rs);
- }
- return ans;
- }
- void dfs_init(int p, int fa, int anc) {
- dfn[p] = ++dfn[0];
- top[p] = anc;
- sz[p] = 1;
- dep[p] = dep[fa] + 1;
- for (int i = G2.head[p]; i; i = G2.nxt[i]) {
- if (G2.to[i] != fa && vis[G2.to[i]] == -1) {
- dfs_init(G2.to[i], p, anc);
- sz[p] += sz[G2.to[i]];
- }
- }
- }
- int main() {
- n = rd();
- for (int i = 1; i <= n; i ++ ) {
- qu[i] = rd();
- G1.add(i, qu[i]);
- d[qu[i]] ++ ;
- }
- toposort();
- for (int i = 1; i <= n; i ++ ) {
- if (vis[i]) {
- G2.add(qu[i], i);
- }
- }
- for (int i = 1; i <= n; i ++ ) {
- if (!vis[i] && !disp[i]) {
- vis[0] ++ ;
- int t = i;
- int now = 0;
- do {
- vis[t] = vis[0];
- disp[t] = true;
- Round[vis[0]].push_back(t);
- Ans[vis[0]].push_back(0);
- now ++ ;
- sk[t] = now - 1;
- dfs_init(t, t, t);
- t = qu[t];
- } while(t != i);
- }
- }
- int m = rd();
- int lastans = 0;
- for (int i = 1; i <= m; i ++ ) {
- int x = rd();
- if (vis[x] == -1) {
- update(dfn[x], 1, 1, n, root[i + dep[x]]);
- lastans = query(dfn[x], dfn[x] + sz[x] - 1, 1, n, root[i + dep[x]]);
- printf("%d\n", lastans);
- Fix[i + dep[x] - 2].push_back(x);
- }
- else {
- int Round_length = Round[vis[x]].size();
- int now_position = ((sk[x] - i + 1) % Round_length + Round_length) % Round_length;
- Ans[vis[x]][now_position] ++ ;
- lastans = Ans[vis[x]][now_position];
- printf("%d\n", lastans);
- }
- int len = Fix[i].size();
- for (int j = 0; j < len; j ++ ) {
- int y = top[Fix[i][j]];
- int Round_length = Round[vis[y]].size();
- int now_position = ((sk[y] - i) % Round_length + Round_length) %Round_length;
- Ans[vis[y]][now_position] ++ ;
- }
- }
- return 0;
- }
这个嘛....是调的时候加的调试信息....博主只会用输出调试所以调完的代码都比较壮观,这个题恶心我两个小时特殊记录一下.....(有兴趣可以看看爽一爽顺便$diss$博主傻逼/xk)
- #include <bits/stdc++.h>
- #define N 500010
- using namespace std;
- struct Edge {
- int to[N << 1], nxt[N << 1], tot, head[N];
- inline void add(int x, int y) {
- to[ ++ tot] = y;
- nxt[tot] = head[x];
- head[x] = tot;
- }
- }G1,G2;
- int d[N], dep[N], qu[N], dfn[N], Q[N], vis[N], sk[N], sz[N], top[N];
- int root[N << 1];
- bool disp[N];
- queue<int> q;
- vector<int> Round[N], Ans[N];
- int cnt;
- vector<int> Fix[N << 1];
- struct Segment_Tree {
- int ls, rs, sum;
- }a[N << 8];
- char *p1, *p2, buf[100000];
- #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
- int rd() {
- int x = 0, f = 1;
- char c = nc();
- while (c < 48) {
- if (c == '-')
- f = -1;
- c = nc();
- }
- while (c > 47) {
- x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
- }
- return x * f;
- }
- int n;
- void toposort() {
- for (int i = 1; i <= n; i ++ ) {
- if (!d[i]) {
- vis[i] = -1;
- q.push(i);
- }
- }
- while (!q.empty()) {
- int x = q.front();
- q.pop();
- for (int i = G1.head[x]; i; i = G1.nxt[i]) {
- d[G1.to[i]] -- ;
- if (!d[G1.to[i]]) {
- q.push(G1.to[i]);
- vis[G1.to[i]] = -1;
- }
- }
- }
- }
- inline void pushup(int x) {
- int ls = a[x].ls, rs = a[x].rs;
- a[x].sum = 0;
- if (ls) {
- a[x].sum += a[ls].sum;
- }
- if (rs) {
- a[x].sum += a[rs].sum;
- }
- }
- void update(int x, int val, int l, int r, int &p) {
- // printf("A %d %d %d %d %d %d\n", x, val, l, r, p, a[p].sum);
- if (!p) {
- p = ++cnt;
- }
- // printf("B %d %d %d %d %d %d\n", x, val, l, r, p, a[p].sum);
- if (l == r) {
- a[p].sum ++ ;
- return;
- }
- // printf("C %d %d %d %d %d %d\n", x, val, l, r, p, a[p].sum);
- int mid = (l + r) >> 1;
- if (x <= mid) {
- update(x, val, l, mid, a[p].ls);
- }
- else {
- update(x, val, mid + 1, r, a[p].rs);
- }
- pushup(p);
- // printf("D %d %d %d %d %d %d\n", x, val, l, r, p, a[p].sum);
- }
- int query(int x, int y, int l, int r, int p) {
- if (!p) {
- return 0;
- }
- if (x <= l && r <= y) {
- return a[p].sum;
- }
- int mid = (l + r) >> 1, ans = 0;
- if (x <= mid) {
- ans += query(x, y, l, mid, a[p].ls);
- }
- if (mid < y) {
- ans += query(x, y, mid + 1, r, a[p].rs);
- }
- return ans;
- }
- void dfs_init(int p, int fa, int anc) {
- dfn[p] = ++dfn[0];
- top[p] = anc;
- sz[p] = 1;
- dep[p] = dep[fa] + 1;
- for (int i = G2.head[p]; i; i = G2.nxt[i]) {
- if (G2.to[i] != fa && vis[G2.to[i]] == -1) {
- dfs_init(G2.to[i], p, anc);
- sz[p] += sz[G2.to[i]];
- }
- }
- }
- int main() {
- n = rd();
- for (int i = 1; i <= n; i ++ ) {
- qu[i] = rd();
- G1.add(i, qu[i]);
- d[qu[i]] ++ ;
- }
- toposort();
- for (int i = 1; i <= n; i ++ ) {
- if (vis[i]) {
- G2.add(qu[i], i);
- }
- }
- // for (int i = 1; i <= n; i ++ ) {
- // printf("%d ",vis[i]);
- // }
- // puts("");
- for (int i = 1; i <= n; i ++ ) {
- if (!vis[i] && !disp[i]) {
- // printf("i-> %d\n", i);
- vis[0] ++ ;
- int t = i;
- int now = 0;
- do {
- // printf("t-> %d\n", t);
- vis[t] = vis[0];
- disp[t] = true;
- Round[vis[0]].push_back(t);
- Ans[vis[0]].push_back(0);
- now ++ ;
- sk[t] = now - 1;
- dfs_init(t, t, t);
- t = qu[t];
- // printf("t-> %d\n", t);
- } while(t != i);
- // while (t != i) {
- // vis[t] = vis[0];
- // disp[t] = true;
- // Round[vis[0]].push_back(t);
- // Ans[vis[0]].push_back(0);
- // now ++ ;
- // sk[t] = now - 1;
- // dfs_init(t, t, t);
- // t = qu[t];
- // }
- }
- }
- // puts("Fuck");
- // printf("%d %d %d %d\n", sk[2], sk[4], sk[7], sk[10]);
- int m = rd();
- int lastans = 0;
- for (int i = 1; i <= m; i ++ ) {
- int x = rd();
- // x ^= lastans;
- // printf("i- %d\n", i);
- if (vis[x] == -1) {
- // printf("A\n");
- // printf("%d\n", i + dep[x]);
- update(dfn[x], 1, 1, n, root[i + dep[x]]);
- lastans = query(dfn[x], dfn[x] + sz[x] - 1, 1, n, root[i + dep[x]]);
- printf("%d\n", lastans);
- // printf("Fix %d\n", i + dep[x] - 2);
- Fix[i + dep[x] - 2].push_back(x);
- }
- else {
- int Round_length = Round[vis[x]].size();
- int now_position = ((sk[x] - i + 1) % Round_length + Round_length) % Round_length;
- // printf("%d %d\n", Round_length, now_position);
- Ans[vis[x]][now_position] ++ ;
- lastans = Ans[vis[x]][now_position];
- printf("%d\n", lastans);
- }
- int len = Fix[i].size();
- for (int j = 0; j < len; j ++ ) {
- // Fix[i][j];
- int y = top[Fix[i][j]];
- int Round_length = Round[vis[y]].size();
- int now_position = ((sk[y] - i) % Round_length + Round_length) %Round_length;
- // printf("np %d\n", now_position);
- // printf("%d %d\n", Round_length, now_position);
- Ans[vis[y]][now_position] ++ ;
- }
- }
- return 0;
- }
- /*
- 6
- 1
- 2
- 1
- 3
- 3
- 6
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
- 6
- 6
- */
- /*
- 1 1 1 1 1 1 5 2 1 1 1 2 9 3 1 1 1 3 4 5
- */
小结:好题好题,这个出成考试题非常妙啊,好想诶,就是有点难写。当然只是对我来讲,别人肯定分分钟切掉。
[Nowcoder113E]弹球弹弹弹_线段树的更多相关文章
- BZOJ_4636_蒟蒻的数列_线段树+动态开点
BZOJ_4636_蒟蒻的数列_线段树+动态开点 Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将 ...
- BZOJ_3252_攻略_线段树+dfs序
BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...
- BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针
BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间, ...
- BZOJ_2124_等差子序列_线段树+Hash
BZOJ_2124_等差子序列_线段树+Hash Description 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pL ...
- BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心
BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心 Description 在计算机中,CPU只能和高速缓存Cache直接交换数据.当所需的内存单元不在Cache中时,则需要从主存里把数 ...
- BZOJ_1828_[Usaco2010 Mar]balloc 农场分配_线段树
BZOJ_1828_[Usaco2010 Mar]balloc 农场分配_线段树 Description Input 第1行:两个用空格隔开的整数:N和M * 第2行到N+1行:第i+1行表示一个整数 ...
- BZOJ_1798_[AHOI2009]维护序列_线段树
BZOJ_1798_[AHOI2009]维护序列_线段树 题意:老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: ( ...
- BZOJ_3307_雨天的尾巴_线段树合并+树上差分
BZOJ_3307_雨天的尾巴_线段树合并 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后 ...
- BZOJ_1858_[Scoi2010]序列操作_线段树
BZOJ_1858_[Scoi2010]序列操作_线段树 Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询 ...
- BZOJ_2957_楼房重建_线段树
BZOJ_2957_楼房重建_线段树 Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多 ...
随机推荐
- Educational Codeforces Round 34 (Rated for Div. 2) B题【打怪模拟】
B. The Modcrab Vova is again playing some computer game, now an RPG. In the game Vova's character re ...
- PHP mysqli_fetch_fields() 函数
mysqli_fetch_fields() 函数返回结果集中代表字段(列)的对象的数组. 返回结果集中代表字段(列)的对象的数组,然后输出每个字段名称.表格和最大长度: <?php // 假定数 ...
- BZOJ 2165: 大楼 倍增Floyd
卡了一上午常数,本地13s,可是bzoj 就是过不去~ #include <bits/stdc++.h> #define N 102 #define M 55 #define ll lon ...
- HGOI 20191103am 题解
Problem A number 使用一个$2^k$数集中每个元素的和表示数$n$,不同集合的数目有多少? 对于$100\%$的数据满足$1 \leq n \leq 10^6$ Solution : ...
- Linux下 Nginx 启动 重启 关闭
命令 nginx -s reload :修改配置后重新加载生效 nginx -s reopen :重新打开日志文件 nginx -t -c /path/to/nginx.conf 测试nginx配置文 ...
- 使用聚集索引和非聚集索引对MySQL分页查询的优化
内容摘录来源:MSSQL123 ,lujun9972.github.io/blog/2018/03/13/如何编写bash-completion-script/ 一.先公布下结论: 1.如果分页排序字 ...
- airflow自动生成dag
def auto_create_dag(): dag_list=[] dag = DAG() dag_list.append(dag) return dag_list dags = auto_crea ...
- 利用简易爬虫完成一道基础CTF题
利用简易爬虫完成一道基础CTF题 声明:本文主要写给新手,侧重于表现使用爬虫爬取页面并提交数据的大致过程,所以没有对一些东西解释的很详细,比如表单,post,get方法,感兴趣的可以私信或评论给我.如 ...
- OUC_Summer Training_ DIV2_#14 724
又落下好多题解啊...先把今天的写上好了. A - Snow Footprints Time Limit:1000MS Memory Limit:262144KB 64bit IO F ...
- RunHelper,一个为跑步而设计的开源的android app
RunHelper是一个为跑步而设计的android应用,意在为爱跑步的人提供一个简洁.实用.免费的工具. 我自己也经常跑步,也用过像Nike running.runkeeper之类的app:Nike ...