洛谷上若干经典树状数组题解集(lg1774lg1966lg1972)
Posted hans774882968
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷上若干经典树状数组题解集(lg1774lg1966lg1972)相关的知识,希望对你有一定的参考价值。
文章目录
lg1774
经典题:排序只能交换相邻元素的最少操作次数。
为什么答案是逆序数:升序数组逆序数为0。一次操作逆序数减少0或1。因此最优方案必定满足:每一步的逆序数都能减少1。证毕。
但我们还是不知道最优方案长啥样!对于a[i] <= a[i+1]
的i
,交换它们是多余的,因为逆序数不减少。因此我们设右侧是已经升序排好的部分,那么我们就需要在左侧找到最大值并把它放到期望的位置(当然还有一些副作用的)。于是我们发现这就是冒泡排序的过程!
这里我们又得到一个经典结论:冒泡排序的交换次数等于逆序数。
lg1966
排序不等式+lg1774的升级版。
优化函数=sum(a[i]^2)+sum(b[i]^2)-2*sum(a[i]*b[i])
,忽略常量,所以需要最大化sum(a[i]*b[i])
。套用排序不等式的结论“正序和最大”即可。
洛谷上面有许多题解,但是把下一步讲清楚的题解真的几乎没有,这里我尝试填补这个空白。
lg1774的升级版
这样得到的问题是lg1774的一个拓展版。那么可以猜测一个拓展版的解答(xs,根本猜不到):
设ia[x]
:a
中排名x
的元素的下标,即rka
的反函数。ib[x]
同理。
设c[x]
:和a[x]
排名相同的b
中元素在b
数组的下标。和a[x]
排名相同的b
中元素目标是在x
位置,所以目标就是c
升序。
c
的求法:rep (i, 1, n) c[i] = ib[rka[i]];
最后看对a的操作怎么映射到对c的操作:a[i]
与a[i+1]
的交换操作对应:
`c[i]后` = 和`a[i+1]原`排名相同的`b`中元素在`b`数组的位置 = `c[i+1]原`。
c[i+1]后
同理。所以a[i]
与a[i+1]
的交换操作等价于c[i]
与c[i+1]
的交换操作。
结论:对c
的操作方案就是对a
的操作方案,而b
不动。
简化一下c数组的求法
枚举排名i
,a[ia[i]]
和b[ib[i]]
的排名显然都是i
,因此有:rep (i, 1, n) c[ia[i]] = ib[i];
代码
朴素版
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
#define lowbit(x) ((x) & (-(x)))
const int N = 1e5 + 5, mod = 1e8 - 3;
int n, a[N], b[N], rka[N], ia[N], ib[N], c[N];
int C[N];
void dbg()
puts ("");
template<typename T, typename... R>void dbg (const T &f, const R &... r)
cout << f << " ";
dbg (r...);
template<typename Type>inline void read (Type &xx)
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
void read()
template<typename T, typename ...R>void read (T &x, R &...r)
read (x);
read (r...);
bool cmpa (int x, int y)
return a[x] < a[y];
bool cmpb (int x, int y)
return b[x] < b[y];
void mdy (int x, int v)
for (; x <= n; x += lowbit (x) ) C[x] += v;
int qry (int x)
int ans = 0;
for (; x; x -= lowbit (x) ) ans += C[x];
return ans;
int main()
read (n);
rep (i, 1, n) read (a[i]);
rep (i, 1, n) read (b[i]);
rep (i, 1, n) ia[i] = ib[i] = i;
sort (ia + 1, ia + n + 1, cmpa);
sort (ib + 1, ib + n + 1, cmpb);
rep (i, 1, n) rka[ia[i]] = i;
rep (i, 1, n) c[i] = ib[rka[i]];
int ans = 0;
dwn (i, n, 1)
ans = (ans + qry (c[i] - 1) ) % mod;
mdy (c[i], 1);
printf ("%d\\n", ans);
return 0;
简化版
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
#define lowbit(x) ((x) & (-(x)))
const int N = 1e5 + 5, mod = 1e8 - 3;
int n, a[N], b[N], ia[N], ib[N], c[N];
int C[N];
void dbg()
puts ("");
template<typename T, typename... R>void dbg (const T &f, const R &... r)
cout << f << " ";
dbg (r...);
template<typename Type>inline void read (Type &xx)
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
void read()
template<typename T, typename ...R>void read (T &x, R &...r)
read (x);
read (r...);
bool cmpa (int x, int y)
return a[x] < a[y];
bool cmpb (int x, int y)
return b[x] < b[y];
void mdy (int x, int v)
for (; x <= n; x += lowbit (x) ) C[x] += v;
int qry (int x)
int ans = 0;
for (; x; x -= lowbit (x) ) ans += C[x];
return ans;
int main()
read (n);
rep (i, 1, n) read (a[i]);
rep (i, 1, n) read (b[i]);
rep (i, 1, n) ia[i] = ib[i] = i;
sort (ia + 1, ia + n + 1, cmpa);
sort (ib + 1, ib + n + 1, cmpb);
rep (i, 1, n) c[ia[i]] = ib[i];
int ans = 0;
dwn (i, n, 1)
ans = (ans + qry (c[i] - 1) ) % mod;
mdy (c[i], 1);
printf ("%d\\n", ans);
return 0;
lg1972
莫队做法略。
关键词:pre数组。
这里介绍的做法:离线+树状数组。
考虑固定r的一系列询问。我们把r右侧的数组忽略掉,并记最后一次出现的数字贡献为1,非最后一次出现的数贡献则为0(这就是pre数组的本质)。实现方式:
if (pre[a[u]]) mdy (pre[a[u]], -1);
mdy (u, 1);
pre[a[u]] = u;
所以最终做法:离线按r排序,r枚举(增大)过程中只需要单点修改,处理r相同的一系列询问即询问一系列区间和。处理这两个需求只需要树状数组。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
#define lowbit(x) ((x) & (-(x)))
const int N = 1e6 + 5;
int n, m, a[N], pre[N], ans[N];
int C[N];
void dbg()
puts ("");
template<typename T, typename... R>void dbg (const T &f, const R &... r)
cout << f << " ";
dbg (r...);
template<typename Type>inline void read (Type &xx)
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
void read()
template<typename T, typename ...R>void read (T &x, R &...r)
read (x);
read (r...);
struct Qry
int l, r, idx;
bool operator < (const Qry &rhs) const
return r < rhs.r;
q[N];
void mdy (int x, int v)
for (; x <= n; x += lowbit (x) ) C[x] += v;
int qry (int x)
int ans = 0;
for (; x; x -= lowbit (x) ) ans += C[x];
return ans;
int main()
read (n);
rep (i, 1, n) read (a[i]);
read (m);
rep (i, 1, m)
int l, r;
read (l, r);
q[i] = l, r, i;
sort (q + 1, q + m + 1);
int u = 1;
rep (i, 1, m)
int r = q[i].r;
while (u <= r)
if (pre[a[u]]以上是关于洛谷上若干经典树状数组题解集(lg1774lg1966lg1972)的主要内容,如果未能解决你的问题,请参考以下文章