1.13 树状数组专练总结

wtclwtcl!!!

T3卡了一个半小时写不出来,结果听评讲是个逆序对(???)

T1 NKOJ4866 和谐数

题目大意:一个长度为n的序列,对于第i个数字,设左边比它大的有x个,右边比它大的有y个,若max(x, y) > 2 * min(x, y),则这个数不和谐。求数列中不和谐数字的总数。

解法:正序一遍树状数组,倒序一遍树状数组即可。

#include <bits/stdc++.h>

using namespace std;

int a[100005], b[100005], c[100005], ans1[100005], ans2[100005], n;

void modify(int x, int p)
{
    for (int i = x; i <= n; i += (i & -i)) c[i] += p;
}
int query(int x)
{
    int sum = 0;
    for (int i = x; i; i -= (i & -i)) sum += c[i];
    return sum;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        b[i] = a[i];
    }
    sort(b + 1, b + n + 1);
    int tot = unique(b + 1, b + n + 1) - b - 1;
    for (int i = 1; i <= n; i++)
    {
        a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b;
    }
    for (int i = 1; i <= n; i++)
    {
        ans1[i] = i - query(a[i]) - 1;
        modify(a[i], 1);
    }
    memset(c, 0, sizeof(c));
    for (int i = n; i; i--)
    {
        ans2[i] = n - i - query(a[i]);
        modify(a[i], 1);
    }
    int sum = 0;
    for (int i = 1; i <= n; i++)
    {
        if (max(ans1[i], ans2[i]) > 2 * min(ans1[i], ans2[i])) sum++;
    }
    printf("%d", sum);
}

T2 NKOJ4224 矩阵计数

题目大意:二维矩阵,单点修改,区间查询,强制在线。

题解:裸的二维树状数组。

#include <bits/stdc++.h>

using namespace std;

int a[305][305], c[305][305][105], n, m;

void modify(int x, int y, int k, int p)
{
    for (int i = x; i <= n; i += (i & -i))
    {
        for (int j = y; j <= m; j += (j & -j))
        {
            c[i][j][k] += p;
        }
    }
}

int query(int x, int y, int k)
{
    int sum = 0;
    for (int i = x; i; i -= (i & -i))
    {
        for (int j = y; j; j -= (j & -j))
        {
            sum += c[i][j][k];
        }
    }
    return sum;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            scanf("%d", &a[i][j]);
            modify(i, j, a[i][j], 1);
        }
    }
    int q; scanf("%d", &q);
    for (int i = 1, type, k; i <= q; i++)
    {
        scanf("%d", &type);
        if (type == 1)
        {
            int x, y;
            scanf("%d%d%d", &x, &y, &k);
            modify(x, y, a[x][y], -1);
            a[x][y] = k;
            modify(x, y, a[x][y], 1);
        }
        else
        {
            int x1, x2, y1, y2;
            scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &k);
            printf("%d\n", query(x2, y2, k) - query(x2, y1 - 1, k) - query(x1 - 1, y2, k) + query(x1 - 1, y1 - 1, k));
        }
    }
}

T3(订正) NKOJ4223 彩色方块

题面:何老板最近在玩一款叫“彩色方块”的小游戏,游戏虽然简单,但何老板仍旧乐此不疲。
游戏中有n个彩色方块成一排,方块的颜色用字母表示,给出目标排列,只要把它们排成跟目标一样的排列,就算过关。每次操作只能交换相邻两个方块。
给出一关游戏,何老板想知道,最少操作几次就能过关,请你帮他计算最少所需的操作步数。

解法:用26个队列给字母编号,然后求逆序对即可。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const ll maxn = 1000005; 

queue <ll> Q[120];

ll a[maxn], b[maxn], c[maxn], d[maxn], mark[maxn], nxt[maxn], head[maxn], n, ans;

#define lowbit(i) i & (-i)

void Modify(ll x, ll k)
{
    for (ll i = x; i <= n; i += lowbit(i)) b[i] += k;
}

ll querysum(ll x)
{
    ll ans = 0;
    for (ll i = x; i; i -= lowbit(i)) ans += b[i];
    return ans;
} 

ll read()
{
    char ch = getchar();
    while (ch < 'A' || ch > 'Z') ch = getchar();
    return (ll) ch - 'A' + 1;
}

main()
{
    scanf("%lld", &n);
    for (ll i = 1; i <= n; i++) c[i] = read();
    for (ll i = 1, x; i <= n; i++) x = read(), Q[x].push(i);
    for (ll i = 1; i <= n; i++)
    {
        d[Q[c[i]].front()] = i;
        Q[c[i]].pop();
    }
    for (ll i = 1; i <= n; i++)
    {
        ans += querysum(n) - querysum(d[i]);
        Modify(d[i], 1);
    }
    printf("%lld", ans);
    return 0;
}

T4:NKOJ4875 前缀前缀和

题目大意:单点修改,求前缀和的前缀和。

题解:就类似树状数组区间修改,区间查询的方式维护即可。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

ll a[100005], c[100005], n, m, d1[100005], d2[100005];

#define lowbit(i) i & (-i)

void modify(ll x, ll k)
{
    for (ll i = x; i <= n; i += lowbit(i)) d1[i] += k, d2[i] += 1LL * k * (x - 1); 
}

ll querysum(ll x)
{
    ll sum1 = 0, sum2 = 0;
    for (ll i = x; i; i -= lowbit(i))
    {
        sum1 += 1LL * d1[i] * x;
        sum2 += d2[i];
    }
    return sum1 - sum2;
}

main()
{
    scanf("%lld%lld", &n, &m);
    for (ll i = 1, x; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        modify(i, a[i]);
    }
    string ch;
    while (m--)
    {
        ll i;
        cin >> ch;
        if (ch[0] == 'Q')
        {
            scanf("%lld", &i);
            ll sum = 0;
            printf("%lld\n", querysum(i)); 
        }
        else
        {
            ll i, d;
            scanf("%lld%lld", &i, &d);
            modify(i, d - a[i]);
            a[i] = d;
        }
    }
}

 

posted @ 2021-01-13 18:52  Chasing-Dreams  阅读(79)  评论(1)    收藏  举报