• bzoj4241 历史研究 分块


    题目传送门

    https://lydsy.com/JudgeOnline/problem.php?id=4241

    题解

    这道题要求出来的就是一个区间带权众数。

    回忆一下一般的区间众数怎么求:

    分块,对于每一次询问 (l, r),如果 (l, r) 不在同一个整块中,那么答案一定是中间的整块的众数,或者两旁的剩下来的数。

    这个东西可以预处理一个 (f[i][j]) 表示从第 (i) 块到 (j) 的众数就可以单次 (sqrt n) 求出了。

    对于两旁的剩数,每个数可以预处理每个值的出现位置的序列,在上面二分求出每个数的出现次数。


    可以发现上面的结论依然可以用于带权众数。

    不过这道题的数据范围大了一点,有 (10^5),所以需要 (O(1)) 求出一个数在某一整段中的出现次数。因为是整段,也就是开头到结尾包含的都是整块,所以可以预处理一个前缀和 (s[i][j]) 表示前 (i) 块中 (j) 的出现次数就可以求出来了。


    伤心,(O((n+q)sqrt n))(O((n+q)sqrt n log n)) 吊打。

    #include<bits/stdc++.h>
    
    #define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
    #define dbg(...) fprintf(stderr, __VA_ARGS__)
    #define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
    #define fi first
    #define se second
    #define pb push_back
    
    template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
    template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
    
    typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
    
    template<typename I> inline void read(I &x) {
    	int f = 0, c;
    	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    	x = c & 15;
    	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    	f ? x = -x : 0;
    }
    
    const int N = 1e5 + 7;
    const int B = 316 + 7;
    
    #define bl(x) (((x) - 1) / blo + 1)
    #define st(x) (((x) - 1) * blo + 1)
    #define ed(x) std::min((x) * blo, n)
    
    int n, dis, Q, blo;
    int a[N], b[N], s[B][N], cnt[N];
    ll f[B][N];
    
    inline void ycl() {
    	for (int i = 1; i <= bl(n); ++i)
    		for (int j = st(i); j <= ed(i); ++j) ++s[i][a[j]];
    	for (int i = 1; i <= bl(n); ++i)
    		for (int j = 1; j <= dis; ++j) s[i][j] += s[i - 1][j];
    	for (int i = 1; i <= bl(n); ++i) {
    		memset(cnt, 0, sizeof(int) * (dis + 1));
    		int x = st(i);
    		ll mx = 0;
    		for (int j = x; j <= n; ++j) {
    			smax(mx, (ll)b[a[j]] * ++cnt[a[j]]);
    			f[i][j] = mx;
    		}
    	}
    	memset(cnt, 0, sizeof(int) * (dis + 1));
    }
    
    inline ll qry(int l, int r) {
    	ll ans = 0;
    	if (bl(l) == bl(r)) {
    		for (int i = l; i <= r; ++i) smax(ans, (ll)b[a[i]] * ++cnt[a[i]]);
    		for (int i = l; i <= r; ++i) cnt[a[i]] = 0;
    		return ans;
    	}
    	ans = f[bl(l) + 1][r];
    	int sl = bl(l) + 1, sr = bl(r) - 1;
    	for (int i = l; i <= ed(bl(l)); ++i) smax(ans, (ll)b[a[i]] * (++cnt[a[i]] + s[sr][a[i]] - s[sl - 1][a[i]]));
    	for (int i = st(bl(r)); i <= r; ++i) smax(ans, (ll)b[a[i]] * (++cnt[a[i]] + s[sr][a[i]] - s[sl - 1][a[i]]));
    	for (int i = l; i <= ed(bl(l)); ++i) cnt[a[i]] = 0;
    	for (int i = st(bl(r)); i <= r; ++i) cnt[a[i]] = 0;
    	return ans;
    }
    
    inline void lsh() {
    	std::sort(b + 1, b + n + 1);
    	dis = std::unique(b + 1, b + n + 1) - b - 1;
    	for (int i = 1; i <= n; ++i) a[i] = std::lower_bound(b + 1, b + dis + 1, a[i]) - b;
    }
    
    inline void work() {
    	lsh();
    	ycl();
    	while (Q--) {
    		int l, r;
    		read(l), read(r);
    		printf("%lld
    ", qry(l, r));
    	}
    }
    
    inline void init() {
    	read(n), read(Q);
    	blo = sqrt(n);
    	for (int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i];
    }
    
    int main() {
    #ifdef hzhkk
    	freopen("hkk.in", "r", stdin);
    #endif
    	init();
    	work();
    	fclose(stdin), fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    分治策略
    uva 11424
    lightoj 1214
    lightoj 1282 && uva 11029
    lightoj 1341
    zoj 2369 Two Cylinders
    几种素数筛法
    lightoj 1245
    hdoj 1299 Diophantus of Alexandria
    求前n项正整数的倒数和
  • 原文地址:https://www.cnblogs.com/hankeke/p/bzoj4241.html
Copyright © 2020-2023  润新知