https://scut.online/p/106

错在这组样例,发现是离散化之后,对k访问的时候也是应该访问离散化之后的k。

12 4
1 1 2 2 5 5 4 4 3 3 2
1 1 3 3 5 7 7 9 9 9 11 11
1 10
3 10
3 11
2 4

发现主席树大概还真的要开够log倍,少一点都不行,那干脆开大一点。

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std; const int MAXN = 100000 + 5; int a[MAXN], b[MAXN];
vector<int> E[MAXN]; int siz[MAXN], tid[MAXN], rnk[MAXN], cnt; int T[MAXN], tcnt;
int sum[MAXN << 5], L[MAXN << 5], R[MAXN << 5]; void init(int n) {
for(int i = 1; i <= n; ++i)
E[i].clear();
cnt = 0;
tcnt = 0;
} void dfs(int u) {
siz[u] = 1;
tid[u] = ++cnt;
rnk[cnt] = u;
for(auto v : E[u]) {
dfs(v);
siz[u] += siz[v];
}
} inline int build(int l, int r) {
int rt = ++tcnt;
sum[rt] = 0;
if(l < r) {
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
}
return rt;
} inline int update(int pre, int l, int r, int x) {
int rt = ++tcnt;
R[rt] = R[pre];
L[rt] = L[pre];
sum[rt] = sum[pre] + 1;
if(l < r) {
if(x <= mid)
L[rt] = update(L[pre], l, mid, x);
else
R[rt] = update(R[pre], mid + 1, r, x);
}
return rt;
} //查询[u-1,v]中不超过k的数的个数
inline int query2(int u, int v, int l, int r, int k) {
int res = 0;
while(l < r && k < r) {
if(k >= mid) {
res += sum[L[v]] - sum[L[u]];
u = R[u], v = R[v], l = mid + 1;
} else
u = L[u], v = L[v], r = mid;
}
return res + (k >= l ? sum[v] - sum[u] : 0);
} int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n, q, r = 1;
while(~scanf("%d%d", &n, &q)) {
init(n);
for(int i = 2, f; i <= n; ++i) {
scanf("%d", &f);
E[f].push_back(i);
}
dfs(r);
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
int cb = unique(b + 1, b + 1 + n) - b - 1;
tcnt = 0;
T[0] = build(1, cb);
for(int i = 1; i <= n; i ++) {
int t = lower_bound(b + 1, b + 1 + cb, a[rnk[i]]) - b;
T[i] = update(T[i - 1], 1, cb, t);
}
while(q--) {
int u, k;
scanf("%d%d", &u, &k);
k = upper_bound(b + 1, b + 1 + cb, k) - b - 1;
int l = tid[u], r = tid[u] + siz[u] - 1;
printf("%d\n", query2(T[l - 1], T[r], 1, cb, k));
}
}
return 0;
}

可以启发式合并Treap。我们知道启发式合并的复杂度确实总共是nlogn级别的。离线所有操作,从叶子向上合并。那么每次询问的时候的确就是在u所在子树查询,因为u节点子树恰好就是u节点的平衡树。还是在方面的万能样例翻车了。是批量插入和删除有点问题。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXN = 120000; int ch[MAXN + 5][2];
int val[MAXN + 5], dat[MAXN + 5];
int siz[MAXN + 5], cnt[MAXN + 5];
ll sum[MAXN + 5];
int tot, root[MAXN + 5]; int st[MAXN + 5], stop; inline void Init(int n) {
tot = 0;
memset(root, 0, sizeof(root[0]) * (n + 1));
stop = 0;
} inline int NewNode(int v) {
if(stop == 0) {
ch[++tot][0] = 0;
ch[tot][1] = 0;
val[tot] = v, dat[tot] = rand();
siz[tot] = 1, cnt[tot] = 1;
sum[tot] = v;
return tot;
} else {
int id = st[stop--];
ch[id][0] = 0;
ch[id][1] = 0;
val[id] = v, dat[id] = rand();
siz[id] = 1, cnt[id] = 1;
sum[id] = v;
return id;
}
} inline void PushUp(int id) {
siz[id] = siz[ch[id][0]] + siz[ch[id][1]] + cnt[id];
sum[id] = sum[ch[id][0]] + sum[ch[id][1]] + 1ll * val[id] * cnt[id];
} inline void Rotate(int &id, int d) {
int temp = ch[id][d ^ 1];
ch[id][d ^ 1] = ch[temp][d];
ch[temp][d] = id;
id = temp;
PushUp(ch[id][d]), PushUp(id);
} inline void Insert(int &id, int v) {
if(!id)
id = NewNode(v);
else {
if(v == val[id])
++cnt[id];
else {
int d = v < val[id] ? 0 : 1;
Insert(ch[id][d], v);
if(dat[id] < dat[ch[id][d]])
Rotate(id, d ^ 1);
}
PushUp(id);
}
} void Remove(int &id, int v) {
if(!id)
return;
else {
if(v == val[id]) {
if(cnt[id] > 1) {
cnt[id] --;
PushUp(id);
} else if(ch[id][0] || ch[id][1]) {
if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]])
Rotate(id, 1), Remove(ch[id][1], v);
else
Rotate(id, 0), Remove(ch[id][0], v);
PushUp(id);
} else{
st[++stop]=id;
id = 0;
}
} else {
v < val[id] ? Remove(ch[id][0], v) : Remove(ch[id][1], v);
PushUp(id);
}
}
} int GetRank(int id, int v) {
//小于等于v的有几个?
if(!id)
return 0;
else {
if(v == val[id])
return siz[ch[id][0]] + cnt[id];
else if(v < val[id])
return GetRank(ch[id][0], v);
else
return siz[ch[id][0]] + cnt[id] + GetRank(ch[id][1], v);
}
} //把id2的树整棵插进id1中
void Merge(int &id1, int &id2) {
if(siz[id1] < siz[id2])
swap(id1, id2);
//将id2的根插进id1,目前每次只搬运一个
while(siz[id2]) {
int tmpv = val[id2]/*, tmpn = cnt[id2]*/;
Remove(id2, tmpv);
Insert(id1, tmpv);
}
return;
} int d[MAXN], f[MAXN], a[MAXN]; int u2topo[MAXN], cnttopo, topo2u[MAXN]; struct Query {
int u, k, ans, id;
bool operator<(const Query& q)const {
return u2topo[u] < u2topo[q.u];
}
} que[MAXN]; struct cmp {
bool operator()(const Query& q1, const Query& q2)const {
return q1.id < q2.id;
}
}; int que2[MAXN];
int front, back; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n, q;
while(~scanf("%d%d", &n, &q)) {
memset(d, 0, sizeof(d[0]) * (n + 1));
for(int i = 2; i <= n; ++i) {
scanf("%d", &f[i]);
++d[f[i]];
}
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
front = 1, back = 0, cnttopo = 0;
for(int i = 1; i <= n; ++i) {
if(d[i] == 0)
que2[++back] = i;
}
while(back >= front) {
int u = que2[front++];
u2topo[u] = ++cnttopo;
topo2u[cnttopo] = u;
d[f[u]]--;
if(d[f[u]] == 0)
que2[++back] = f[u];
}
for(int i = 1; i <= q; ++i) {
scanf("%d%d", &que[i].u, &que[i].k);
que[i].id = i;
}
sort(que + 1, que + 1 + q);
Init(n);
for(int i = 1; i <= n; ++i) {
Insert(root[i], a[i]);
//cout<<siz[root[i]]<<endl;
//cout<<u2topo[i]<<" ";
}
//cout<<endl;
int curtopo = 1;
for(int i = 1; i <= q; ++i) {
while(u2topo[que[i].u] > curtopo) {
//cout<<"Qu="<<que[i].u<<" topoQu="<<u2topo[que[i].u]<<endl;
//等于的话不用合并,直接查询
int u = topo2u[curtopo++];
//cout<<"Merge "<<u<<" "<<f[u]<<endl;
Merge(root[f[u]], root[u]);
//cout<<" After Merge Size="<<siz[root[f[u]]]<<endl;
}
//现在相等了
que[i].ans = GetRank(root[que[i].u], que[i].k);
/*cout<<"Answer u="<<que[i].u;
cout<<" ans="<<que[i].ans<<endl;*/
}
sort(que + 1, que + 1 + q, cmp());
for(int i = 1; i <= q; ++i) {
printf("%d\n", que[i].ans);
}
//puts("---");
}
return 0;
}

是NewNode没有传入num的问题,但是为什么恰好开n个位置会RE了呢?(因为后面的MAXN没有加5!以后还是在MAXN里面+5,这样数组会好看一点)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXN = 120000; int ch[MAXN + 5][2];
int val[MAXN + 5], dat[MAXN + 5];
int siz[MAXN + 5], cnt[MAXN + 5];
int tot, root[MAXN + 5]; int st[MAXN + 5], stop; inline void Init(int n) {
tot = 0;
memset(root, 0, sizeof(root[0]) * (n + 1));
stop = 0;
} inline int NewNode(int v, int num) {
int id = ((stop==0) ? ++tot : st[stop--]);
ch[id][0] = 0;
ch[id][1] = 0;
val[id] = v, dat[id] = rand();
siz[id] = num, cnt[id] = num;
return id;
} inline void PushUp(int id) {
siz[id] = siz[ch[id][0]] + siz[ch[id][1]] + cnt[id];
} inline void Rotate(int &id, int d) {
int temp = ch[id][d ^ 1];
ch[id][d ^ 1] = ch[temp][d];
ch[temp][d] = id;
id = temp;
PushUp(ch[id][d]), PushUp(id);
} inline void Insert(int &id, int v, int num) {
if(!id)
id = NewNode(v, num);
else {
if(v == val[id])
cnt[id] += num;
else {
int d = v < val[id] ? 0 : 1;
Insert(ch[id][d], v, num);
if(dat[id] < dat[ch[id][d]])
Rotate(id, d ^ 1);
}
PushUp(id);
}
} void Remove(int &id, int v, int num) {
if(!id)
return;
else {
if(v == val[id]) {
if(cnt[id] > num) {
cnt[id -= num];
PushUp(id);
} else if(ch[id][0] || ch[id][1]) {
if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]])
Rotate(id, 1), Remove(ch[id][1], v, num);
else
Rotate(id, 0), Remove(ch[id][0], v, num);
PushUp(id);
} else {
st[++stop] = id;
id = 0;
}
} else {
v < val[id] ? Remove(ch[id][0], v, num) : Remove(ch[id][1], v, num);
PushUp(id);
}
}
} int GetRank(int id, int v) {
//小于等于v的有几个?
if(!id)
return 0;
else {
if(v == val[id])
return siz[ch[id][0]] + cnt[id];
else if(v < val[id])
return GetRank(ch[id][0], v);
else
return siz[ch[id][0]] + cnt[id] + GetRank(ch[id][1], v);
}
} //把id2的树整棵插进id1中
void Merge(int &id1, int &id2) {
if(siz[id1] < siz[id2])
swap(id1, id2);
//将id2的根插进id1,目前每次只搬运一个
while(siz[id2]) {
int tmpv = val[id2], tmpn = cnt[id2];
Remove(id2, tmpv, tmpn);
Insert(id1, tmpv, tmpn);
}
return;
} int d[MAXN], f[MAXN], a[MAXN]; int u2topo[MAXN], cnttopo, topo2u[MAXN]; struct Query {
int u, k, ans, id;
bool operator<(const Query& q)const {
return u2topo[u] < u2topo[q.u];
}
} que[MAXN]; struct cmp {
bool operator()(const Query& q1, const Query& q2)const {
return q1.id < q2.id;
}
}; int que2[MAXN];
int front, back; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n, q;
while(~scanf("%d%d", &n, &q)) {
memset(d, 0, sizeof(d[0]) * (n + 1));
for(int i = 2; i <= n; ++i) {
scanf("%d", &f[i]);
++d[f[i]];
}
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
front = 1, back = 0, cnttopo = 0;
for(int i = 1; i <= n; ++i) {
if(d[i] == 0)
que2[++back] = i;
}
while(back >= front) {
int u = que2[front++];
u2topo[u] = ++cnttopo;
topo2u[cnttopo] = u;
d[f[u]]--;
if(d[f[u]] == 0)
que2[++back] = f[u];
}
for(int i = 1; i <= q; ++i) {
scanf("%d%d", &que[i].u, &que[i].k);
que[i].id = i;
}
sort(que + 1, que + 1 + q);
Init(n);
for(int i = 1; i <= n; ++i) {
Insert(root[i], a[i], 1);
//cout<<siz[root[i]]<<endl;
//cout<<u2topo[i]<<" ";
}
//cout<<endl;
int curtopo = 1;
for(int i = 1; i <= q; ++i) {
while(u2topo[que[i].u] > curtopo) {
//cout<<"Qu="<<que[i].u<<" topoQu="<<u2topo[que[i].u]<<endl;
//等于的话不用合并,直接查询
int u = topo2u[curtopo++];
//cout<<"Merge "<<u<<" "<<f[u]<<endl;
Merge(root[f[u]], root[u]);
//cout<<" After Merge Size="<<siz[root[f[u]]]<<endl;
}
//现在相等了
que[i].ans = GetRank(root[que[i].u], que[i].k);
/*cout<<"Answer u="<<que[i].u;
cout<<" ans="<<que[i].ans<<endl;*/
}
sort(que + 1, que + 1 + q, cmp());
for(int i = 1; i <= q; ++i) {
printf("%d\n", que[i].ans);
}
//puts("---");
}
return 0;
}

SCUT - 106 - 花式ac - 主席树/启发式合并Treap的更多相关文章

  1. Bzoj2534:后缀自动机 主席树启发式合并

    国际惯例的题面:考虑我们求解出字符串uvu第一个u的右端点为i,第二个u的右端点为j,我们需要满足什么性质?显然j>i+L,因为我们选择的串不能是空串.另外考虑i和j的最长公共前缀(也就是说其p ...

  2. 【BZOJ-3123】森林 主席树 + 启发式合并

    3123: [Sdoi2013]森林 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2738  Solved: 806[Submit][Status] ...

  3. P3302 [SDOI2013]森林(主席树+启发式合并)

    P3302 [SDOI2013]森林 主席树+启发式合并 (我以前的主席树板子是错的.......坑了我老久TAT) 第k小问题显然是主席树. 我们对每个点维护一棵包含其子树所有节点的主席树 询问(x ...

  4. [bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT

    Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...

  5. 【主席树 启发式合并】bzoj3123: [Sdoi2013]森林

    小细节磕磕碰碰浪费了半个多小时的时间 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M ...

  6. BZOJ_3123_[Sdoi2013]森林_主席树+启发式合并

    BZOJ_3123_[Sdoi2013]森林_主席树+启发式合并 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20 ...

  7. 【bzoj3123】[Sdoi2013]森林 倍增LCA+主席树+启发式合并

    题目描述 输入 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...

  8. Bzoj 3123: [Sdoi2013]森林(主席树+启发式合并)

    3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MB Description Input 第一行包含一个正整数testcase,表示当前 ...

  9. Bzoj 3673: 可持久化并查集 by zky(主席树+启发式合并)

    3673: 可持久化并查集 by zky Time Limit: 5 Sec Memory Limit: 128 MB Description n个集合 m个操作 操作: 1 a b 合并a,b所在集 ...

随机推荐

  1. Redis---系统学习

    1.安装Redis Docker 2.查看Redis配置 进入Docker中的Redis容器: 进入启动命令目录:cd /usr/local/bin/ 启动redis客户端:./redis-cli c ...

  2. java调用存储过程的方式

    1.问号是入参和出参,出参要指定类型 CallableStatement pstmt = conn.prepareCall("{call dbo.UP_CodeUp_***(?,?,?,?, ...

  3. Software-Defined Networking: A Comprehensive Survey

    文章名称:Software-Defined Networking: A Comprehensive Survey 文章来源:Proceedings of the IEEE ( Volume: 103  ...

  4. Android实现无标题栏全屏的三种方法

    一.通过Java代码 在setContentView之前执行: requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题栏 getWindow().se ...

  5. 深入理解Spring(一):初识Spring

    深入理解Spring(一):初识Spring 一. Spring介绍        Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnso ...

  6. influxDB 1.3 中文文档

    influxDB是一个旨在处理高并发写入和查询负载的时序数据库,它是TICK框架的第二部分,influxdb用于任何包含大量时序数据应用的后台存储,包括Devops监控.应用指标数据.物联网传感器数据 ...

  7. Linux中相关知识(atexit(),fork(),粘滞位)

    1.atexit()函数 函数名: atexit 头文件:#include<stdlib.h> 功 能: 注册终止函数(即main执行结束后调用的函数) 用 法: int atexit(v ...

  8. wowza 降低延迟

    转自:http://www.ttstream.com/wowza/live/howToAchieveTheLowestLatencyFromCaptureToPlayback   这篇文章介绍了在用R ...

  9. PJSIP库设置Via地址

    好记性不如烂笔头,解决项目问题,调试代码跟踪到PJSIP 设置Via地址位置,记录下来,以备后用. PJSIP库在方法stateless_send_transport_cb中设置Via地址值,该方法在 ...

  10. FOUC(Flash Of Unstyled Content)文档样式闪烁

    今天看面试题看到了这个新名词..我以前是没有发现过这种状况,应该是我一直都是将加载 CSS 的 link 标签写到 head 里的缘故吧. 什么是文档样式闪烁(Flash Of Unstyled Co ...