zoukankan      html  css  js  c++  java
  • 【BZOJ4241】历史研究 分块

    【BZOJ4241】历史研究

    Description

    IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
    日记中记录了连续N天发生的时间,大约每天发生一件。
    事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大。
    JOI教授决定用如下的方法分析这些日记:
    1. 选择日记中连续的一些天作为分析的时间段
    2. 事件种类t的重要度为t*(这段时间内重要度为t的事件数)
    3. 计算出所有事件种类的重要度,输出其中的最大值
    现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

    Input

    第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。
    接下来一行N个空格分隔的整数X1...XN,Xi表示第i天发生的事件的种类
    接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数Ai和Bi,表示第i次询问的区间为[Ai,Bi]。

    Output

    输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度

    Sample Input

    5 5
    9 8 7 8 9
    1 2
    3 4
    4 4
    1 4
    2 4

    Sample Output

    9
    8
    8
    16
    16

    HINT

    1<=N<=10^5
    1<=Q<=10^5
    1<=Xi<=10^9 (1<=i<=N)

    题解:第一眼看题都感觉是莫队吧?然而听说这题卡莫队。

    这题的本质是一个带权的区间众数问题,如果你会做区间众数,那么这道题也不难(当然不会做也无所谓)。

    先分块,假设[a,b]是[l,r]中一段连续的整块,那么[l,r]中的带权众数∈([a,b]中的众数 U [l,a)和(b,r]中的所有数)。那么我们预处理出任意两个块(a,b)之间的带权众数(方法:枚举左边的块,在暴力扫一遍右边的所有块,用桶记录一下,维护个最大值),以及[1,a]中所有数出现的次数。处理询问时,先直接拿出中间整块的众数,再枚举两边小块的所有数即可。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int maxn=100010;
    int n,m,nm,B;
    ll ans;
    struct node
    {
    	int val,org;
    }num[maxn];
    int v[maxn],ref[maxn],st[maxn],s[400][maxn];
    ll mx[400][400];
    bool cmp(node a,node b)
    {
    	return a.val<b.val;
    }
    int rd()
    {
    	int ret=0;	char gc=getchar();
    	while(gc<'0'||gc>'9')	gc=getchar();
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret;
    }
    int main()
    {
    	n=rd(),m=rd();
    	B=int(sqrt(double(n)));
    	int i,j,k,a,b,c,d;
    	for(i=0;i<n;i++)	num[i].val=rd(),num[i].org=i;
    	sort(num,num+n,cmp);
    	for(ref[0]=-1,i=0;i<n;i++)
    	{
    		if(num[i].val>ref[nm])	ref[++nm]=num[i].val;
    		v[num[i].org]=nm;
    	}
    	for(i=0;i<n;i++)	s[i/B][v[i]]++;
    	for(i=1;i*B<n;i++)	for(j=1;j<=nm;j++)	s[i][j]+=s[i-1][j];
    	for(i=0;i*B<n;i++)
    	{
    		for(j=i;j*B<n;j++)
    		{
    			mx[i][j]=mx[i][j-1];
    			for(k=j*B;k<j*B+B&&k<n;k++)	st[v[k]]++,mx[i][j]=max(mx[i][j],(ll)st[v[k]]*ref[v[k]]);
    		}
    		memset(st,0,sizeof(st));
    	}
    	for(i=1;i<=m;i++)
    	{
    		a=rd()-1,b=rd()-1,c=a/B,d=b/B,ans=0;
    		if(c==d)
    		{
    			for(j=a;j<=b;j++)	st[v[j]]++,ans=max(ans,(ll)st[v[j]]*ref[v[j]]);
    			for(j=a;j<=b;j++)	st[v[j]]=0;
    			printf("%lld
    ",ans);
    			continue;
    		}
    		ans=mx[c+1][d-1];
    		for(j=a;j<c*B+B;j++)	st[v[j]]=s[d-1][v[j]]-s[c][v[j]];
    		for(j=d*B;j<=b;j++)	st[v[j]]=s[d-1][v[j]]-s[c][v[j]];
    		for(j=a;j<c*B+B;j++)	st[v[j]]++,ans=max(ans,(ll)st[v[j]]*ref[v[j]]);
    		for(j=d*B;j<=b;j++)	st[v[j]]++,ans=max(ans,(ll)st[v[j]]*ref[v[j]]);
    		for(j=a;j<c*B+B;j++)	st[v[j]]=0;
    		for(j=d*B;j<=b;j++)	st[v[j]]=0;
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
  • 相关阅读:
    3288 积木大赛
    3284 疯狂的黄大神
    1531 山峰
    1018 单词接龙
    1432 总数统计
    1507 酒厂选址
    1063 合并果子
    几个sort不能过的题目
    poj 2245 Lotto
    求两圆相交面积模板
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7123448.html
Copyright © 2011-2022 走看看