简单分块

分析

分成若干块,每块预处理,暴力合并

数据范围一般有点特别

预处理有两类方法:

  1. 预处理连续的块之间的答案
  2. 预处理出单独块的答案,然后\(O(\frac{n}{S})\)大力合并

例题1

Luogu P4135 作诗

采用分块,分完后暴力求出连续的块的答案\(O(n\sqrt n)\),以及块中元素的前缀和\(O(C\sqrt n)\)

对于每次询问,加入完整块的答案,暴力搞不完整块,时间复杂度是\(O(n\sqrt n)\)

WA了一次:预处理是下标写错了

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5,M=320;
int n,m,c,S,bl[N],s[M][N],f[M][M],t[N],a[N];
int main() {
	scanf("%d%d%d",&n,&c,&m);
	S=sqrt(n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		bl[i]=(i-1)/S+1;
	}
	for(int i=1;i<=n;i+=S) {
		for(int j=1;j<=c;j++) s[bl[i]][j]=s[bl[i]-1][j];
		for(int j=i;j<=min(i+S-1,n);j++) {
			s[bl[i]][a[j]]++;
		}
	}
	int ans=0;
	for(int i=1;i<=n;i+=S) {
		ans=0;
		for(int j=bl[i];j<=bl[n];j++) {
			for(int k=S*(j-1)+1;k<=min(S*j,n);k++) {
				if(t[a[k]]>0&&t[a[k]]%2==0) {
					ans--;
				}
				t[a[k]]++;
				if(t[a[k]]%2==0) ans++;
			}
			f[bl[i]][j]=ans;
		}
		memset(t,0,sizeof(t));
	}
	ans=0;
	while(m--) {
		int l,r; scanf("%d%d",&l,&r),l=(l+ans)%n+1,r=(r+ans)%n+1;
		if(l>r)swap(l,r); 
		int t1=bl[l],t2=bl[r];
		if(t2-t1<=1) {
			ans=0;
			for(int j=l;j<=r;j++) {
				if(t[a[j]]>0&&t[a[j]]%2==0) {
					ans--;
				} 
				t[a[j]]++;
				if(t[a[j]]%2==0) {
					ans++;
				}
			}
			printf("%d\n",ans);
			for(int j=l;j<=r;j++) {
				t[a[j]]--;
			}
			continue;
		}
		ans=f[t1+1][t2-1];
		for(int i=l;i<=t1*S;i++) {
			if(!t[a[i]]) {
				t[a[i]]=s[t2-1][a[i]]-s[t1][a[i]];
			}	
			if(t[a[i]]>0&&t[a[i]]%2==0) {
				ans--;
			}
			t[a[i]]++;
			if(t[a[i]]%2==0) ans++; 
		}
		for(int i=(t2-1)*S+1;i<=r;i++) {
			if(!t[a[i]]) {
				t[a[i]]=s[t2-1][a[i]]-s[t1][a[i]];
			}	
			if(t[a[i]]>0&&t[a[i]]%2==0) {
				ans--;
			}
			t[a[i]]++;
			if(t[a[i]]%2==0) ans++; 
		}
		printf("%d\n",ans);
		for(int i=l;i<=t1*S;i++) {
			t[a[i]]=0;
		}
		for(int i=(t2-1)*S+1;i<=r;i++) {
			t[a[i]]=0;
		}
	}
	return 0;
}

例题2

ybtoj 工作评估

糟糕,是心动的感觉,是以前做过的比赛里的压轴(zhou\)题

还记得当时写完暴力后不知所措的样子

还记得黄队讲的时候被dui了无耐地表示没写过

设区间答案为\(f[i,j,x0]\)(太妙了)

显然\(x0\)越大答案越优,且\(x0\)到达一定峰值后答案将不再变化

所以,我们引进函数\(g[i,j]\)表示\(f[i,j,INF]\),即到达天花板时的答案,\(s[i,j]\)表示\(\sum_{k=i}^{j}D[k]\)

如果\(x0\)能到达天花板,则\(f[i,j,x0]\)=\(g[i,j]\),此时\(g[i,j]\leq s[i,j]+x0\)

但如果\(x0\)不能,,则\(f[i,j,x0]\)=\(s[i,j]+x0\),此时\(g[i,j]\geq s[i,j]+x0\)

\[\therefore f[i,j,x0]=min(g[i,j],s[i,j]+x0) \]

考虑优化:如果\(g[i1,j1]\leq g[i2,j2],s[i1,j1]\leq s[i2,j2]\)\((i1,j1)\)没用

所以将\(g\)从小到大排序后,\(s\)也应该是有序的,且从大到小

可以发现\(f\)是一个单峰函数,且在\(g,s\)大小关系变化处取到极值,可以二分

所以对每块维护里面所有的\(g,s\)

但块和块之间呢,显然不能预处理了,采用方法2

对于每块只有5种情况

  1. 全在块内(预处理)
  2. 走到块为止(记块前的最大值mx,预处理前缀)
  3. 从块出发(预处理后缀,求出最大值)
  4. 经过块(预处理块的答案,暴力给mx)
  5. 不经过块()

接下来是简单~的代码实现

WA了1次:预处理后缀时真就从后往前预处理,像个憨憨(顺序和答案有关啊)

TLE了1次:直接用vector传递变量(时间复杂度是vector),像个憨憨,前面加&就行了

#include <bits/stdc++.h>
#define ll long long
const int INF = 1e9;
using namespace std;

const int N = 5e4 + 5, M = 230;
int n, S, bl[N], b[M], a[N], L[N];
ll s[N];
struct A {
    int x, y;
};
vector<A> V, f[M], pre[M], suf[M];
inline bool cmp(A i, A j) { return i.x < j.x || i.x == j.x && i.y < j.y; }

int ask(vector<A> &t, int x) {
    int l = 0, r = t.size() - 1, ret = 0;
    while (l <= r) {
        int mid = l + r >> 1;
        if (t[mid].y + x - t[mid].x > 0) {
            ret = mid, l = mid + 1;
        } else
            r = mid - 1;
    }
    if (ret == t.size() - 1) {
        return min(t[ret].x, t[ret].y + x);
    }
    return max(min(t[ret].x, t[ret].y + x), min(t[ret + 1].x, t[ret + 1].y + x));
}

void bld(vector<A>& t) {
    sort(V.begin(), V.end(), cmp);
    int num = V.size();
    t.push_back(V[0]);
    int sz = 1;
    for (int j = 1; j < num; j++) {
        while (sz && t[sz - 1].y <= V[j].y) {
            t.pop_back();
            sz--;
        }
        t.push_back(V[j]), sz++;
    }
}
int main() {
    int T;
    scanf("%d%d", &n, &T), S = sqrt(n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]), s[i] = s[i - 1] + a[i];
        bl[i] = (i - 1) / S + 1;
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &L[i]);
    }
    for (int i = 1; i <= n; i += S) {
        V.clear();
        for (int j = i; j <= min(i + S - 1, n); j++) {
            int t = INF;
            for (int k = j; k <= min(i + S - 1, n); k++) {
                t = min(t + a[k], L[k]);
                V.push_back((A){ t, min((ll)INF, s[k] - s[j - 1]) });
            }
        }
        bld(f[bl[i]]);
        V.clear();
        int t = INF;
        for (int j = i; j <= min(i + S - 1, n); j++) {
            t = min(t + a[j], L[j]);
            V.push_back((A){ t, min((ll)INF, s[j] - s[i - 1]) });
        }
        b[bl[i]] = t;
        bld(pre[bl[i]]);
        V.clear();
        for (int j = i; j <= min(i + S - 1, n); j++) {
            t = INF;
            for (int k = j; k <= min(i + S - 1, n); k++) {
                t = min(t + a[k], L[k]);
            }
            V.push_back((A){ t, min((ll)INF, s[min(i + S - 1, n)] - s[j - 1]) });
        }
        bld(suf[bl[i]]);
    }
    while (T--) {
        int l, r, x;
        scanf("%d%d%d", &l, &r, &x);
        int t1 = bl[l], t2 = bl[r];
        if (t1 == t2) {
            int ans = 0, t = x;
            for (int i = l; i <= r; i++) {
                t = min(t + a[i], L[i]);
                t = max(t, x);
                ans = max(ans, t);
            }
            printf("%d\n", ans);
            continue;
        }
        int ans = 0, t = x, mx = 0;
        for (int i = l; i <= t1 * S; i++) {
            t = min(t + a[i], L[i]);
            t = max(t, x);
            ans = max(ans, t);
        }
        mx = t;
        for (int i = t1 + 1; i <= t2 - 1; i++) {
            ans = max(ans, ask(f[i], x));
            ans = max(ans, ask(pre[i], mx));
            t = min((ll)b[i], min((ll)INF, s[i * S] - s[(i - 1) * S] + mx));
            ans = max(ans, t);
            mx = ask(suf[i], x);
            mx = max(mx, t);
            mx = max(mx, x);
        }
        t = mx;
        for (int i = (t2 - 1) * S + 1; i <= r; i++) {
            t = min(t + a[i], L[i]);
            t = max(t, x);
            ans = max(ans, t);
        }
        printf("%d\n", ans);
    }
    return 0;
}

例题3

ybtoj F. 颜色权值

显然也是分块

预处理出前缀和,块和块之间的颜色小于K的答案

时间复杂度是\(O((\frac{n}{S})^2n+nS)\),取\(S=n^{\frac{2}{3}}\)即可

posted @ 2021-03-31 16:51  wwwsfff  阅读(59)  评论(0)    收藏  举报