20231121

2023/11/21

树状数组

t[i],为树状数组;a[i],为原数组

t[i]代表的区间为a(i-lowbit(i)+1)~a(i)这个区间。

所以求前缀的时候,每次-=lowbit(x),区间是连续接起来的

修改操作,a[x]+val,原数组单点加,那么我们要去树状数组上找哪些节点包含a[x],所以是一个+=lowbit(x)的过程

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define Acode ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
const int N = 1e6 + 10;
int t[N];
int n, m;
int a[N];

int lowbit(int x)
{
    return x & (-x);
}

void add(int x, int val)  //对a[x]+val,同时修改t数组,t是真实意义上的树状数组
{
    while (x <= n)
    {
        t[x] += val;
        x += lowbit(x);
    }
}

int getsum(int x)    // 计算sum[1,x]  1到x的前缀和 ,a[1]..a[x]的和
{
    int sum = 0;
    while (x)
    {
        sum += t[x];
        x -= lowbit(x);
    }
    return sum;
}

int query(int l, int r)   //区间求和  , 前缀相减
{
    return getsum(r) - getsum(l - 1);
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        add(i, a[i]);
    }
    for (int i = 1; i <= m; i++)
    {
        int op, x, y;
        cin >> op >> x >> y;
        if (op == 1)
        {
            add(x, y);
        }
        else
        {
            cout << query(x, y) << endl;
        }
    }
}

树状数组上二分

因为2的幂次的t[i]数组(即i为2的幂次),他表示的区间就是1~i,所以可以利用位运算的贪心策略从高到底的求最大的区间(第一次的区间表示)

t[i]代表的区间为a(i-lowbit(i)+1)~a(i)这个区间。(关键)

O(n*logn)查询小于x时,前缀的最大下标

const int N = 2e6 + 10;
int t[N];
int n, m;
int a[N];

int lowbit(int x)
{
    return x & (-x);
}

void add(int x, int val)
{
    while (x <= n)
    {
        t[x] += val;
        x += lowbit(x);
    }
}

int getsum(int x)
{
    int sum = 0;
    while (x)
    {
        sum += t[x];
        x -= lowbit(x);
    }
    return sum;
}

int query(int s)
{
    int c = 0;   // 记录当前的前缀和
    int pos = 0; // 记录位置
    for (int j = 20; j >= 0; j--)
    {
        if (pos + (1LL << j) <= n && c + t[pos + (1LL << j)] <= s)
        {
            pos += (1LL << j);
            c += t[pos];
        }
    }
    return pos;
}

void solve()
{
    int q;
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        add(i, a[i]);
    }
    while (q--)
    {
        int op;
        cin >> op;
        if (op == 1)
        {
            int x, d;
            cin >> x >> d;
            add(x, d - a[x]); // 修改a[x]为d
            a[x] = d;
        }
        else
        {
            int s;
            cin >> s;
            cout << query(s) << endl;
        }
    }
}

二维树状数组

对每一个一位的树状数组节点都看成一个树状数组,这样就形成了二维的树状数组,高位同理

二维树状数组 1:单点修改,区间查询

const int N = 4100;
long long t[N][N];
int n, m;

int lowbit(int x)
{
    return x & (-x);
}

void add(int x, int y, int val)
{
    for (int p = x; p <= n; p += p & (-p))
    {
        for (int q = y; q <= m; q += q & (-q))
        {
            t[p][q] += val;
        }
    }
}

int query(int x, int y)
{
    int sum = 0;
    for (int p = x; p; p -= p & (-p))
    {
        for (int q = y; q; q -= q & (-q))
        {
            sum += t[p][q];
        }
    }
    return sum;
}

void solve()
{
    cin >> n >> m;
    int op;
    while (cin >> op)
    {
        if (op == 1)
        {
            int x, y, k;
            cin >> x >> y >> k;
            add(x, y, k);
        }
        else
        {
            int a, b, c, d;
            cin >> a >> b >> c >> d;
            cout << query(c, d) - query(c, b - 1) - query(a - 1, d) + query(a - 1, b - 1) << endl;
        }
    }
}

CF1800F Dasha and Nightmares

思路:首先抓住25个字母的关键,少一个是什么意思,不就是给我枚举少了哪一个用的嘛

我们枚举缺哪个字母,对每一个字符串处理一下它少了哪些字母,放进集合里面。然后我们可以把奇数次出现的字母看作1,偶数次或没有出现的看作0.等价处理,这样我们就可以得到一个字符串的二进制值,最多为2的26次,int存的下。然后我们对缺少的字母的集合枚举。遍历里面的元素,然后开个桶记录一下这样我们可以O(1)的求出符合条件的对数。

具体做法为,新遍历到的一个元素,然后我们可以知道和他能匹配的二进制是多少,由于我们之前桶记录过,所以ans+=mp[id]即可。总复杂度为O(26*n);

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define Acode ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
// #define int long long
const int N = 2e5 + 10;
int n, m;
int vis[30];
vector<int> g[30];
int c[N];
// map<int, int> mp;
int mp[(1 << 26) + 10];
void solve()
{
    int k = (1LL << 26) - 1;
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= 26; j++)
            vis[j] = 0;
        string s;
        cin >> s;
        int len = s.size();
        s = " " + s;
        for (int j = 1; j <= len; j++)
        {
            vis[s[j] - 'a']++;
        }
        for (int j = 0; j < 26; j++)
        {
            if (!vis[j])
                g[j].push_back(i);
        }
        int x = 0;
        for (int j = 0; j < 26; j++)
        {
            if (vis[j] % 2)
                x += (1LL << j);
        }
        c[i] = x;
    }
    // cerr << "TTTTT\n";
    long long ans = 0;
    for (int i = 0; i < 26; i++)
    {
        for (auto x : g[i])
        {
            int id = c[x] ^ k ^ (1LL << i);
            ans += mp[id];
            mp[c[x]]++;
        }
        for (auto x : g[i])
            mp[c[x]]--;
        // mp.clear();
    }
    cout << ans << endl;
}

signed main()
{
    Acode;
    int T = 1;
    while (T--)
    {
        solve();
    }
    return 0;
}
posted @ 2023-11-21 22:31  ヤ﹎句号悠灬  阅读(47)  评论(0)    收藏  举报