弹球弹弹弹

题目大意:有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]弹球弹弹弹_线段树的更多相关文章

  1. BZOJ_4636_蒟蒻的数列_线段树+动态开点

    BZOJ_4636_蒟蒻的数列_线段树+动态开点 Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将 ...

  2. BZOJ_3252_攻略_线段树+dfs序

    BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...

  3. BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针

    BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间, ...

  4. BZOJ_2124_等差子序列_线段树+Hash

    BZOJ_2124_等差子序列_线段树+Hash Description 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pL ...

  5. BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心

    BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心 Description 在计算机中,CPU只能和高速缓存Cache直接交换数据.当所需的内存单元不在Cache中时,则需要从主存里把数 ...

  6. BZOJ_1828_[Usaco2010 Mar]balloc 农场分配_线段树

    BZOJ_1828_[Usaco2010 Mar]balloc 农场分配_线段树 Description Input 第1行:两个用空格隔开的整数:N和M * 第2行到N+1行:第i+1行表示一个整数 ...

  7. BZOJ_1798_[AHOI2009]维护序列_线段树

    BZOJ_1798_[AHOI2009]维护序列_线段树 题意:老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: ( ...

  8. BZOJ_3307_雨天的尾巴_线段树合并+树上差分

    BZOJ_3307_雨天的尾巴_线段树合并 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后 ...

  9. BZOJ_1858_[Scoi2010]序列操作_线段树

    BZOJ_1858_[Scoi2010]序列操作_线段树 Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询 ...

  10. BZOJ_2957_楼房重建_线段树

    BZOJ_2957_楼房重建_线段树 Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多 ...

随机推荐

  1. WebService如何封装XML请求 以及解析接口返回的XML

    原 WebService如何封装XML请求 以及解析接口返回的XML 置顶 2019年08月16日 15:00:47 童子泛舟 阅读数 28 标签: XML解析WebService第三方API 更多 ...

  2. nextAll([expr]) 查找当前元素之后所有的同辈元素。

    nextAll([expr]) 概述 查找当前元素之后所有的同辈元素. 可以用表达式过滤 参数 exprStringV1.2 用来过滤的表达式 示例 描述: 给第一个div之后的所有元素加个类直线电机 ...

  3. no suitable method found to override

    no suitable method found to override http://bbs.csdn.net/topics/200001058 

  4. javascript原型继承

    在传统的基于Class的语言如Java.C++中,继承的本质是扩展一个已有的Class,并生成新的Subclass. 由于这类语言严格区分类和实例,继承实际上是类型的扩展.但是,JavaScript由 ...

  5. Jenkins发布.Net Core项目到IIS

    安装Java8,Git,和Jenkins及插件. jenkins安装后以windows服务的方式运行,浏览器访问本机8080端口可访问. 自动部署的原理分为三步,首先从git服务器获取最新代码,然后用 ...

  6. CSS简单选择器的学习笔记

    我们知道通过CSS定义页面样式的时候要用到各种各样的选择器,正确的使用选择器是我们能够正确使用CSS做页面样式的基础.下面是我学习选择器的一个简易笔记,举一些简单的例子. 为了方便展示,我选择在内部的 ...

  7. 【BZOJ3098】 Hash Killer II

    BZOJ3098 Hash Killer II Solution 这道题目好像题面里面给了提示(当然没给就有点难想了.) 曾经讲过一个叫做生日悖论的,不知道还有多少人记得 考虑相同的可能性大概是\(\ ...

  8. 笔记一(固件、BIOS、UEFI)

    1.固件 固件一般是指保存在ROM中的程序和数据,通过固件操作系统按照标准的设备驱动实现特定机器的运行. 简单来讲,固件就是固化在ROM的软件,当然也可以通过特定的工具进行升级. MP3.MP4.手机 ...

  9. word2vec原理与代码

    目录 前言 CBOW模型与Skip-gram模型 基于Hierarchical Softmax框架的CBOW模型 基于Negative Sampling框架的CBOW模型 负采样算法 结巴分词 wor ...

  10. CefGlue获取网页源代码

    1.编写一个CefStringVisitor类: public class MyStringVisitor : CefStringVisitor { private readonly TaskComp ...