洛谷上若干经典树状数组题解集(lg1774lg1966lg1972)

Posted hans774882968

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷上若干经典树状数组题解集(lg1774lg1966lg1972)相关的知识,希望对你有一定的参考价值。

文章目录

juejin传送门

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数组的求法

枚举排名ia[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)的主要内容,如果未能解决你的问题,请参考以下文章

LG5367 「模板」康托展开 康托展开

题解 LG P2264

LIS (nlogn)

洛谷 P3374 模板树状数组 1 题解

究竟为什么,快速排序的时间复杂度是n*lg(n)? | 经典面试题

LG2145 「JSOI2007」祖码 区间DP