博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【树状数组】【P4113】[HEOI2012]采花
阅读量:5889 次
发布时间:2019-06-19

本文共 3126 字,大约阅读时间需要 10 分钟。

Description

给定一个长度为 \(n\) 的序列,有 \(m\) 次询问,每次询问一段区间,求区间中有多少个数出现次数超过 \(1\)

Limitation

\(n,~m~\leq~2~\times~10^6\)

Solution

好像和大众做法不大一样?

考虑对于每个位置,我们记一个 pre 和一个 post,代表他前面最后一个出现这个数的位置和他后面第一个出现这个数的位置。

考虑一个位置 \(x\) 对一个 \([l,~r]\) 的查询产生贡献当且仅当 \(pre_x~\geq~l\) 并且 \(post_x~>~r\)

我们发现这个东西是可以树状数组维护的。

考虑统计一段区间内 pre 大于等于某个数的个数显然可以对 pre 开权值树状数组,然后按照左端点不升序排序,左端点左移的时候将对应位置的 pre 加入树状数组,一次查询的答案即为 \(BIT_{r}~-~BIT_{l - 1}\)

现在考虑加入 \(post_x ~>~r\) 的限制。

我们发现当且仅当满足该条件的位置才对答案有贡献,于是我们只需要保证树状数组里只存有满足该条件的位置的 pre,就可以统计答案了。考虑按照右端点不升序排序,一开始先将最后一次出现的数字的 pre 加入到树状数组中,每次移动右端点的时候,将 post 为右端点所在位置的位置的 pre 加入树状数组即可(即将 \(post_x~=~R\)\(x\) 的对应 \(per_x\) 加入树状数组)。由于大于右端点的部分的 pre 对答案同样没有贡献了,所以移动右端点的时候别忘了把右端点对应的 pre 在树状数组上删除。

Code

#include 
#include
#include
#ifdef ONLINE_JUDGE#define freopen(a, b, c)#endiftypedef long long int ll;namespace IPT { const int L = 1000000; char buf[L], *front=buf, *end=buf; char GetChar() { if (front == end) { end = buf + fread(front = buf, 1, L, stdin); if (front == end) return -1; } return *(front++); }}template
inline void qr(T &x) { char ch = IPT::GetChar(), lst = ' '; while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar(); while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar(); if (lst == '-') x = -x;}namespace OPT { char buf[120];}template
inline void qw(T x, const char aft, const bool pt) { if (x < 0) {x = -x, putchar('-');} int top=0; do {OPT::buf[++top] = static_cast
(x % 10 + '0');} while (x /= 10); while (top) putchar(OPT::buf[top--]); if (pt) putchar(aft);}const int maxn = 2000010;int n, c, m, dn;int MU[maxn], oc[maxn], pos[maxn], pre[maxn], tree[maxn];std::vector
vc[maxn];struct Ask { int l, r, id, ans; inline bool operator<(const Ask &_others) const { return this->r > _others.r; }};Ask ask[maxn];bool cmp(const Ask&, const Ask&);int lowbit(int);void update(int, int);int query(int);int main() { freopen("1.in", "r", stdin); qr(n); qr(c); qr(m); dn = n + 1; for (int i = 1; i <= n; ++i) { qr(MU[i]); pre[i] = oc[MU[i]]; oc[MU[i]] = i; } for (int i = 1; i <= c; ++i) oc[i] = dn; for (int i = n; i; --i) { vc[pos[i] = oc[MU[i]]].push_back(i); oc[MU[i]] = i; } for (int i = 1; i <= m; ++i) { qr(ask[i].l); qr(ask[i].r); ask[i].id = i; } std::sort(ask + 1, ask + 1 + m); for (auto i : vc[dn]) update(pre[i], 1); for (int i = 1, R = n; i <= m; ++i) { int l = ask[i].l, r = ask[i].r; while (R > r) { update(pre[R], -1); for (auto j : vc[R]) update(pre[j], 1); --R; } ask[i].ans = query(r) - query(l - 1); } std::sort(ask + 1, ask + 1 + m, cmp); for (int i = 1; i <= m; ++i) { qw(ask[i].ans, '\n', true); } return 0;}inline bool cmp(const Ask &_a, const Ask &_b) { return _a.id < _b.id;}inline int lowbit(int x) {return x & -x;}void update(int x, int v) { if (x == 0) return; while (x <= dn) { tree[x] += v; x += lowbit(x); }}int query(int x) { int _ret = 0; while (x) { _ret += tree[x]; x -= lowbit(x); } return _ret;}

转载于:https://www.cnblogs.com/yifusuyi/p/10512857.html

你可能感兴趣的文章
python中实参包括哪些_第50p,形参与实参,Python中函数的参数详解
查看>>
minio 并发数_MinIO 参数解析与限制
查看>>
eap wifi 证书_用openssl为EAP-TLS生成证书(CA证书,服务器证书,用户证书)
查看>>
mysql 应用程序是哪个文件夹_Mysql 数据库文件存储在哪个目录?
查看>>
mysql半同步和无损复制_MySQL半同步复制你可能没有注意的点
查看>>
mysql能看见表显示表不存在_遇到mysql数据表不存在的问题
查看>>
使用mysql实现宿舍管理_JSP+Struts2+JDBC+Mysql实现的校园宿舍管理系统
查看>>
mysql alter 修改字段类型_MySQL ALTER命令:删除,添加或修改表字段、修改字段类型及名称等...
查看>>
mysql中的事务和锁_MySQL - 事务和锁中的互斥?
查看>>
mysql statement讲解_Statement接口详解
查看>>
mysql_print_default_知识点:MySQL常用工具介绍(十 二)——实用程序my_print_defaults、perror...
查看>>
mysql怎么会报错_MySQL启动报错怎么办?
查看>>
python编译exe用于别的电脑上_Python安装教程(推荐一款不错的Python编辑器)
查看>>
flash back mysql_mysqlbinlog flashback 使用最佳实践
查看>>
hive中如何把13位转化为时间_sqoop1 导入 hive parquet 表中 时间戳调整为日期
查看>>
mysql书外键_[转] mysql 外键(Foreign Key)的详解和实例
查看>>
mysql存储引擎模式_MySQL存储引擎
查看>>
python入门小游戏代码_【Python】Python代码实现“FlappyBird”小游戏
查看>>
云服务器怎么卸载mysql数据库_mysql 删除数据库脚本
查看>>
mysql 5.5.57互为主从_MYSQL 5.5.18 互为主从配置成功
查看>>