分块题集

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

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

原文地址:https://www.cnblogs.com/littlepear/p/9485468.html