• BZOJ4241: 历史研究


    题目地址

    题目链接

    题解

    经典大分块..不过挺好写的。就写了半个小时左右。
    处理出

    • (ans[i][j])表示块(i)到块(j)的答案
    • (cnt[i][j])表示块(1)到块(i)中数(j)的出现次数(要先离散化)

    预处理这两个数组都离散化后利用一个桶就可以了。
    考虑每个询问([l,r])的答案可能的来源,首先肯定(ans[l][r])是其中的一个候选项,然后两个边界块中的数也都是可能的候选项。那么枚举这两个块中的数,利用(cnt)数组和一个桶即可解决。
    复杂度(O(n sqrt{n}))

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    const int N = 100010;
    const int M = 320;
    
    inline void read(int &x) {
    	x = 0; char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9') {
    		x = x * 10 + c - '0';
    		c = getchar();
    	}
    } 
    
    ll ans[M][M];
    int bl[N], L[M], R[M], cnt[M][N];
    int a[N], b[N], c[N], tong[N];
    int n, m, block, siz;
    
    void init() {
    	siz = sqrt(n);
    	block = n / siz;
    	if(n % siz) ++block;
    	for(int i = 1; i <= n; ++i) {
    		bl[i] = (i - 1) / siz + 1;
    	}
    	for(int i = 1; i <= block; ++i) {
    		L[i] = (i - 1) * siz + 1;
    		R[i] = i * siz;
    	}
    	R[block] = n;
    	for(int i = 1; i <= block; ++i) {
    		for(int j = L[i]; j <= R[i]; ++j) cnt[i][a[j]]++;
    	}
    	for(int j = 1; j <= n; ++j) for(int i = 1; i <= block; ++i) 
    			cnt[i][j] += cnt[i - 1][j];
    	for(int i = 1; i <= block; ++i) {
    		for(int k = L[i]; k <= R[i]; ++k) {
    			tong[a[k]]++;
    			ans[i][i] = max(ans[i][i], 1ll * tong[a[k]] * c[k]);
    		}
    		for(int j = i + 1; j <= block; ++j) {
    			ans[i][j] = ans[i][j - 1];
    			for(int k = L[j]; k <= R[j]; ++k) {
    				tong[a[k]]++;
    				ans[i][j] = max(ans[i][j], 1ll * tong[a[k]] * c[k]);
    			}
    		}
    		for(int j = L[i]; j <= n; ++j) tong[a[j]] = 0;
    	}
    }
    
    ll solve(int l, int r) {
    	ll Ans = 0;
    	if(bl[l] == bl[r] || bl[r] == bl[l] + 1) {
    		for(int i = l; i <= r; ++i) {
    			tong[a[i]]++;
    			Ans = max(Ans, 1ll * tong[a[i]] * c[i]);
    		}
    		for(int i = l; i <= r; ++i) tong[a[i]] = 0;
    		return Ans;
    	}
    	Ans = ans[bl[l] + 1][bl[r] - 1];
    	for(int i = l; i <= R[bl[l]]; ++i) {
    		tong[a[i]]++;
    		Ans = max(Ans, 1ll * (tong[a[i]] + cnt[bl[r] - 1][a[i]] - cnt[bl[l]][a[i]]) * c[i]);
    	}
    	for(int i = L[bl[r]]; i <= r; ++i) {
    		tong[a[i]]++;
    		Ans = max(Ans, 1ll * (tong[a[i]] + cnt[bl[r] - 1][a[i]] - cnt[bl[l]][a[i]]) * c[i]);
    	}
    	for(int i = l; i <= R[bl[l]]; ++i) tong[a[i]] = 0;
    	for(int i = L[bl[r]]; i <= r; ++i) tong[a[i]] = 0;
    	return Ans;
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
    freopen("data.in","r",stdin);
    #endif
    	read(n); read(m);
    	for(int i = 1; i <= n; ++i) {
    		read(a[i]);
    		b[i] = c[i] = a[i];
    	}
    	sort(b + 1, b + n + 1);
    	for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
    	init();
    	while(m--) {
    		int l, r; read(l); read(r);
    		printf("%lld
    ", solve(l, r));
    	}
    }
    
  • 相关阅读:
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1068:与指定数字相同的数的个数
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1068:与指定数字相同的数的个数
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1067:整数的个数
    SSLZYC 2405 巧克力
    SSLZYC 2404 上学
    SSLZYC 2403 蜡烛
    SSLZYC 2402 世界语
    SSLZYC 2124 涂色
    SSLZYC 2391 数列
    SSLZYC 洛谷 P1498 南蛮图腾
  • 原文地址:https://www.cnblogs.com/henry-1202/p/11812373.html
Copyright © 2020-2023  润新知