zoukankan      html  css  js  c++  java
  • 分块题集

    记得分块还是大一时学的,后来就没怎么写过,趁此时机,再重新巩固一下此专题。

    1.普通分块题目

    POJ 3468

    后悔没有学习这么优秀的代码,自己当年写的就是一坨屎。

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 50;
    int L[maxn], R[maxn]; ///每个区间范围
    int pos[maxn]; ///记录所属块
    ll sum[maxn], a[maxn];
    ll add[maxn]; ///相当于lazy
    void change(int l, int r, int d)
    {
        int p = pos[l], q = pos[r];
        if(p == q) ///所属同一块
        {
            for(int i = l; i <= r; i++) a[i] += d;
            sum[p] += (ll)(r - l + 1) * d;
        }
        else ///不同块
        {
            for(int i = p + 1; i <= q - 1; i++) add[i] += d;
            for(int i = l; i <= R[p]; i++) a[i] += d;
            sum[p] += (ll)(R[p] - l + 1) * d;
            for(int i = L[q]; i <= r; i++) a[i] += d;
            sum[q] += (ll)(r - L[q] + 1) * d;
        }
    }
    ll query(int l, int r)
    {
        int p = pos[l], q = pos[r];
        ll ans = 0;
        if(p == q) ///同一块
        {
            for(int i = l; i <= r; i++) ans += a[i];
            ans += add[p] * (r - l + 1);
        }
        else ///不同块
        {
            for(int i = p + 1; i <= q - 1; i++) ans += sum[i] + add[i] * (R[i] - L[i] + 1);
            for(int i = l; i <= R[p]; i++) ans += a[i];
            ans += add[p] * (R[p] - l + 1);
            for(int i = L[q]; i <= r; i++) ans += a[i];
            ans += add[q] * (r - L[q] + 1);
        }
        return ans;
    }
    int main()
    {
        int n, q;
        scanf("%d %d", &n, &q);
        for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        int t = sqrt(n);
        for(int i = 1; i <= t; i++)
        {
            L[i] = (i - 1) * t + 1;
            R[i] = i * t;
        }
        if(R[t] < n) t++, L[t] = R[t - 1] + 1, R[t] = n; ///即使剩下的比sqrt(n)大也无所谓
        for(int i = 1; i <= t; i++)
        {
            for(int j = L[i]; j <= R[i]; j++)
            {
                pos[j] = i;
                sum[i] += a[j];
            }
        }
        while(q--)
        {
            char s[5];
            int l, r, v;
            scanf("%s", s);
            if(s[0] == 'Q')
            {
                scanf("%d %d", &l, &r);
                printf("%lld
    ", query(l, r));
            }
            else
            {
                scanf("%d %d %d", &l, &r, &v);
                change(l, r, v);
            }
        }
        return 0;
    }
    Code

    BZOJ 2821 作诗

    这个一定要会算块大小呀,块大小不正确就是AC和TLE的区别呀!

    假设每一块的大小都是$x$,那么分成$frac{n}{x}$个块,预处理的复杂度是$n imes frac{n}{x}$,询问的复杂度是$m imes x imes log(n)$,$log(n)$是每一个数字二分的复杂度,假设$m$和$n$同大小,那么最后复杂度是$n imes ( frac{n}{x} + x imes log(n))$,令加号两边相等,得$x=sqrt(frac{n}{log(n)})$。

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    using namespace std;
    const int maxn = 1e5 + 50;
    const int block = 3000;
    int L[block], R[block], num[block][block]; ///记录区间的答案
    int pos[maxn];
    int cnt[maxn], last[maxn];
    vector<int> fu[maxn];
    int a[maxn];
    inline char nc(){
        static char buf[100000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int _read(){
        char ch=nc();int sum=0;
        while(!(ch>='0'&&ch<='9'))ch=nc();
        while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
        return sum;
    }
    int query(int i, int l, int r)
    {
        int pos1 = lower_bound(fu[i].begin(), fu[i].end(), l) - fu[i].begin();
        int pos2 = upper_bound(fu[i].begin(), fu[i].end(), r) - fu[i].begin();
        return (pos2 - pos1) >= 0 ? (pos2 - pos1) : 0;
    }
    int main()
    {
        int n, c, m;
        n = _read(), c = _read(), m = _read();
        for(int i = 1; i <= n; i++)
        {
            a[i] = _read();
            fu[a[i]].push_back(i);
        }
        int t = sqrt((double)n / log(n) * log(2)); ///每个块的大小
        int tot = n / t;
        for(int i = 1; i <= tot; i++)
        {
            L[i] = (i - 1) * t + 1;
            if(i == tot) R[i] = n;
            else R[i] = i * t;
        }
       // printf("%d %d
    ", t, tot);
        for(int i = 1; i <= tot; i++)
        {
            num[i][i - 1] = 0;
            for(int j = i; j <= tot; j++)
            {
                num[i][j] = num[i][j - 1];
                for(int k = L[j]; k <= R[j]; k++)
                {
                    pos[k] = j;
                    if(last[a[k]] < i) ///有了last,就不用每次清空cnt数组了
                    {
                        last[a[k]] = i;
                        cnt[a[k]] = 1;
                    }
                    else cnt[a[k]]++;
    
                    if(cnt[a[k]] % 2 == 0) num[i][j]++;
                    else if(cnt[a[k]] > 1) num[i][j]--;
                   // printf("%d %d %d
    ", i, j, num[i][j]);
                }
            }
        }
        memset(last, 0, sizeof(last));
        int ans = 0;
        int k = 0;
        while(m--)
        {
            k++;
            int l, r; l = _read(), r = _read();
            l = (l + ans) % n + 1;
            r = (r + ans) % n + 1;
            if(l > r) swap(l, r);
            int p = pos[l], q = pos[r];
           // printf("%d %d %d %d
    ", l, r, p, q);
    
            ans = 0;
            if(p == q)
            {
                for(int i = l; i <= r; i++)
                {
                    if(last[a[i]] < k)
                    {
                        last[a[i]] = k;
                        ans += (query(a[i], l, r) & 1 ^ 1);
                    }
                }
                printf("%d
    ", ans);
            }
            else
            {
                ans = num[p + 1][q - 1];
                for(int i = l; i <= R[p]; i++)
                {
                    if(last[a[i]] < k)
                    {
                        last[a[i]] = k;
                        int x = query(a[i], L[p + 1], R[q - 1]);
                        if(!x || (x & 1))
                        {
                            ans += (query(a[i], l, r) & 1 ^ 1);
                        }
                        else ///之前为正偶数
                        {
                            ans -= (query(a[i], l, r) & 1);
                        }
                    }
                }
                for(int i = L[q]; i <= r; i++)
                {
                    if(last[a[i]] < k)
                    {
                        last[a[i]] = k;
                        int x = query(a[i], L[p + 1], R[q - 1]);
                        if(!x || (x & 1))
                        {
                            ans += (query(a[i], l, r) & 1 ^ 1);
                        }
                        else ///之前为正偶数
                        {
                            ans -= (query(a[i], l, r) & 1);
                        }
                    }
                }
                printf("%d
    ", ans);
            }
        }
        return 0;
    }
    Code

    BZOJ 弹飞绵羊

    分块后,在每一块内记录通过几步弹出这个块,以及弹出这个块后的位置。

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    using namespace std;
    const int maxn = 2e5 + 50;
    int a[maxn];
    int sum[maxn];
    int nex[maxn];
    int L[maxn], R[maxn];
    int pos[maxn];
    int t;
    void change(int j, int d)
    {
        a[j] = d;
        int p = pos[j];
        for(int i = j; i >= L[p]; i--)
        {
            if(i + a[i] > R[p])
            {
                sum[i] = 1;
                nex[i] = i + a[i];
            }
            else
            {
                sum[i] = sum[i + a[i]] + 1;
                nex[i] = nex[i + a[i]];
            }
        }
    }
    int n, m;
    int ask(int j)
    {
        int ans = 0;
        while(1)
        {
            ans += sum[j];
            int nexpos = nex[j];
            if(nexpos > n) break;
            j = nexpos;
        }
        return ans;
    }
    int main()
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        t = sqrt(n);
        for(int i = 1; i <= t; i++)
        {
            L[i] = (i - 1) * t + 1;
            if(i == t) R[i] = n;
            else R[i] = i * t;
        }
        ///预处理
        for(int i = 1; i <= t; i++)
        {
            for(int j = R[i]; j >= L[i]; j--)
            {
                pos[j] = i;
                if(j + a[j] > R[i])
                {
                    sum[j] = 1;
                    nex[j] = j + a[j];
                }
                else
                {
                    sum[j] = sum[j + a[j]] + 1;
                    nex[j] = nex[j + a[j]];
                }
            }
        }
        scanf("%d", &m);
        while(m--)
        {
            int op, j, k;
            scanf("%d", &op);
            if(op == 1)
            {
                scanf("%d", &j);
                j++;
                if(j > n) printf("0
    ");
                else printf("%d
    ", ask(j));
            }
            else
            {
                scanf("%d %d", &j, &k);
                j++;
                change(j, k);
            }
        }
    }
    /*4
    
    1 2 1 1
    412
    2 0 10
    1 0
    2 0 1
    2 1 10
    1 0
    */
    Code

     2.树上分块题目

    Codeforces Round #199 (Div. 2) E. Xenia and Tree

    这种分块还是挺有意思的,等红点到达一定数量后,再重新建图。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 50;
    const int INF = 0x3f3f3f3f;
    vector<int> red;
    queue<int> q;
    int vis[maxn];
    int d[maxn];
    int n, m;
    
    ///加边
    int cnt, h[maxn];
    struct edge
    {
        int to, pre, v;
    } e[maxn << 1];
    void init()
    {
        cnt = 0;
        memset(h, 0, sizeof(h));
    }
    void add(int from, int to, int v)
    {
        cnt++;
        e[cnt].pre = h[from]; ///5-->3-->1-->0
        e[cnt].to = to;
        e[cnt].v = v;
        h[from] = cnt;
    }
    ///LCA
    int dist[maxn];
    int dep[maxn];
    int anc[maxn][33]; ///2分的父亲节点
    void dfs(int u, int fa)
    {
        for(int i = h[u]; i; i = e[i].pre)
        {
            int v = e[i].to;
            if(v == fa) continue;
            dist[v] = dist[u] + e[i].v;
            dep[v] = dep[u] + 1;
            anc[v][0] = u;
            dfs(v, u);
        }
    }
    void LCA_init(int n)
    {
        for(int j = 1; (1 << j) < n; j++)
            for(int i = 1; i <= n; i++) if(anc[i][j-1])
            anc[i][j] = anc[anc[i][j-1]][j-1];
    }
    int LCA(int u, int v)
    {
        int log;
        if(dep[u] < dep[v]) swap(u, v);
        for(log = 0; (1 << log) < dep[u]; log++);
        for(int i = log; i >= 0; i--)
            if(dep[u] - (1 << i) >= dep[v]) u = anc[u][i];
        if(u == v) return u;
        for(int i = log; i >= 0; i--)
            if(anc[u][i] && anc[u][i] != anc[v][i])
                u = anc[u][i], v = anc[v][i];
        return anc[u][0];
    }
    int calc(int u, int v)
    {
        return dist[u] + dist[v] - 2 * dist[LCA(u, v)];
    }
    void bfs()
    {
        while(!q.empty()) q.pop();
       // for(int i = 1; i <= n; i++) vis[i] = 0;
        for(int i = 0; i < (int)red.size(); i++)
        {
            int u = red[i];
            d[u] = 0;
           // vis[u] = 1;
            q.push(u);
        }
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            for(int i = h[u]; i; i = e[i].pre)
            {
                int v = e[i].to;
                if(d[v] > d[u] + 1)
                {
                    //vis[v] = 1;
                    d[v] = d[u] + 1;
                    q.push(v);
                }
            }
        }
    }
    int main()
    {
        memset(d, INF, sizeof(d));
        init();
        scanf("%d %d", &n, &m);
        for(int i = 1; i < n; i++)
        {
            int u, v; scanf("%d %d", &u, &v);
            add(u, v, 1), add(v, u, 1);
        }
        red.push_back(1);
        bfs();
        red.clear();
        dfs(1, -1);
        LCA_init(n);
        int block = sqrt(n);
        for(int i = 1; i <= m; i++)
        {
            int t, v; scanf("%d %d", &t, &v);
            if(t == 1)
            {
                red.push_back(v);
                if(((int)red.size()) == block)
                {
                    bfs();
                    red.clear();
                }
            }
            else
            {
                int ans = d[v];
                for(int j = 0; j < (int)red.size(); j++)
                {
                    ans = min(ans, calc(v, red[j]));
                }
                printf("%d
    ", ans);
            }
        }
        return 0;
    }
    Code

    HDU 4366 

    和我想象中的树上分块不太一样,按DFS序走完后,就是普通的序列呀。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn =5e4 + 50;
    vector<int> g[maxn];
    int loy[maxn], abi[maxn];
    int in[maxn], out[maxn];
    int ind = 0;
    int match[maxn];
    int rec[maxn];
    void dfs(int u)
    {
        in[u] = ++ind;
        rec[ind] = ind;
        match[ind] = u;
        for(int i = 0; i < g[u].size(); i++)
        {
            int v = g[u][i];
            dfs(v);
        }
        out[u] = ind;
    }
    int t;
    int L[maxn], R[maxn];
    bool cmp(int a, int b)
    {
        int va = match[a];
        int vb = match[b];
        if(abi[va] != abi[vb]) return abi[va] < abi[vb];
        else return loy[va] > loy[vb];
    }
    int maxloy[maxn];
    int pos[maxn];
    int query(int v, int l, int r)
    {
        int p = pos[l], q = pos[r];
        int ans = -1;
        int tmploy = 0;
        if(p == q)
        {
            for(int i = l; i <= r; i++) ///不够整段,就暴力枚举
            {
                if(abi[match[i]] > abi[v])
                {
                    if(loy[match[i]] > tmploy)
                    {
                        ans = match[i];
                        tmploy = loy[match[i]];
                    }
                }
            }
        }
        else
        {
            for(int i = p + 1; i <= q - 1; i++)
            {
                int l = L[i], r = R[i];
                int tmp = 0;
                while(l <= r)
                {
                    int mid = (l + r) / 2;
                    int u = match[rec[mid]];
                    if(abi[u] > abi[v])
                    {
                        tmp = maxloy[mid];
                        r = mid - 1;
                    }
                    else
                    {
                        l = mid + 1;
                    }
                }
                if(loy[tmp] > tmploy)
                {
                    ans = tmp;
                    tmploy = loy[tmp];
                }
            }
            for(int i = l; i <= R[p]; i++)
            {
                if(abi[match[i]] > abi[v])
                {
                    if(loy[match[i]] > tmploy)
                    {
                        ans = match[i];
                        tmploy = loy[match[i]];
                    }
                }
            }
            for(int i = L[q]; i <= r; i++)
            {
                if(abi[match[i]] > abi[v])
                {
                    if(loy[match[i]] > tmploy)
                    {
                        ans = match[i];
                        tmploy = loy[match[i]];
                    }
                }
            }
        }
       // printf("%d
    ", ans);
        return ans;
    }
    int main()
    {
        //freopen("a.txt", "r", stdin);
       // freopen("wrong.txt", "w", stdout);
        int T; scanf("%d", &T);
        while(T--)
        {
            ind = 0;
            int n, m; scanf("%d %d", &n, &m);
            for(int i = 0; i < n; i++) g[i].clear();
            loy[0] = 1e6 + 5, abi[0] = 1e6 + 5;
            for(int i = 1; i <= n - 1; i++)
            {
                int a; scanf("%d %d %d", &a, &loy[i], &abi[i]);
                g[a].push_back(i);
            }
            dfs(0);
            t = sqrt(ind);
            for(int i = 1; i <= t; i++)
            {
                if(i == 1) L[i] = 2;
                else L[i] = (i - 1) * t + 1;
                if(i == t) R[i] = ind;
                else R[i] = i * t;
            }
           // printf("%d
    ", t);
            ///预处理
            for(int i = 1; i <= t; i++)
            {
                sort(rec + L[i], rec + R[i] + 1, cmp);  ///排序这里要注意
                for(int j = R[i]; j >= L[i]; j--)
                {
                    pos[j] = i;
                    if(j == R[i]) maxloy[j] = match[rec[j]]; 
                    else
                    {
                        int v = match[rec[j]];
                        if(loy[v] > loy[maxloy[j + 1]]) ///从后往前记录最大的忠诚度对应的人
                        {
                            maxloy[j] = v;
                        }
                        else
                        {
                            maxloy[j] = maxloy[j + 1];
                        }
                    }
                }
            }
            while(m--)
            {
    
                int fired; scanf("%d", &fired);
                printf("%d
    ", query(fired, in[fired], out[fired]));
            }
        }
        return 0;
    }
    /*
    312
    12 10
    0 976 11305
    0 20237 7816
    1 18781 12029
    3 23992 24775
    4 31529 8581
    0 24804 14400
    3 20559 5900
    6 26811 17570
    4 4405 10627
    7 8046 27226
    5 899 16169
    1
    */
    Code

    HDU 6394 Tree

  • 相关阅读:
    禅道admin忘记密码
    redis conf 解析
    MySQL 安装
    Centos7上安装docker (抄)
    Linux查看CPU和内存使用情况 抄
    上传图片到阿里云OSS和获取上传图片的外网url的步骤
    docker mysql
    Oracle 11g,exp导出时空表、少表的解决办法
    使用com.aspose.words将word模板转为PDF乱码解决方案
    oracle数据库,检索出某几个字段不唯一的那些数据
  • 原文地址:https://www.cnblogs.com/littlepear/p/9485468.html
Copyright © 2011-2022 走看看