【bzoj4241】 历史研究

http://www.lydsy.com/JudgeOnline/problem.php?id=4241 (题目链接)

看到题目就联想到了【bzoj2809】 Apio2012—dispatching。想了想权值分块+莫队,发现不好维护块内最值,又看了看80s的时间,于是怒水一发线段树+莫队,结果先WA后TLE,不断TLE,无论怎么改常数都不行,难道nlogn*sqrt(n)就是过不了吗!!不爽,蒯个题解,再见!

题意:求区间加权众数。

solution 
  貌似是分块,离散化之后,用mx[i][j]表示第i块到第j块的答案,cnt[i][j]表示前i块数字j的个数。

代码:

// bzoj4241
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
inline LL getint() {
    int f,x=0;char ch=getchar();
    while (ch<=‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1;else f=1;ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

const int maxn=100010;
struct data {int w,id;}a[maxn];
int b[maxn],pos[maxn],L[400],R[400],cnt[400][maxn],num[maxn],st[maxn];
LL mx[400][400],ans;
int n,m,block,tot,top;

bool cmp(data a,data b) {
    return a.w<b.w;
}
LL cal(int l,int r) {
    LL ans=0;
    top=0;
    for (int i=l;i<=r;i++) {
        if (!num[b[i]]) st[++top]=b[i];
        num[b[i]]++;
        ans=max(ans,(LL)num[b[i]]*a[b[i]].w);
    }
    while (top) num[st[top--]]=0;
    return ans;
}
void build() {
    block=sqrt(n);tot=(n-1)/block+1;
    for (int i=1;i<=n;i++) {
        pos[i]=(i-1)/block+1;
        if (!L[pos[i]]) L[pos[i]]=i;
        R[pos[i]]=i;
    }
    for (int i=1;i<=n;i++) cnt[pos[i]][b[i]]++;
    for (int i=2;i<=tot;i++)
        for (int j=1;j<=n;j++) cnt[i][j]+=cnt[i-1][j];
}
int main() {
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i].w),a[i].id=i;
    sort(a+1,a+1+n,cmp);
    b[a[1].id]=1;
    for (int i=2;i<=n;i++) {
        if (a[i].w==a[i-1].w) b[a[i].id]=b[a[i-1].id];
        else b[a[i].id]=i;
    }
    build();
    for (int i=1;i<=tot;i++) {
        LL ans=0;
        memset(num,0,sizeof(num));
        for (int j=L[i];j<=n;j++) {
            num[b[j]]++;
            ans=max(ans,(LL)num[b[j]]*a[b[j]].w);
            if (j==R[pos[j]]) mx[i][pos[j]]=ans;
        }
    }
    memset(num,0,sizeof(num));
    for (int i=1;i<=m;i++) {
        int l,r;
        scanf("%d%d",&l,&r);
        ans=0;
        if (pos[l]==pos[r]) printf("%lld\n",cal(l,r));
        else {
            ans=mx[pos[l]+1][pos[r]-1];
            top=0;
            for (int i=l;i<=R[pos[l]];i++)
                if (!num[b[i]]) st[++top]=b[i],num[b[i]]=cnt[pos[r]-1][b[i]]-cnt[pos[l]][b[i]];
            for (int i=L[pos[r]];i<=r;i++)
                if (!num[b[i]]) st[++top]=b[i],num[b[i]]=cnt[pos[r]-1][b[i]]-cnt[pos[l]][b[i]];
            for (int i=l;i<=R[pos[l]];i++) num[b[i]]++;
            for (int i=L[pos[r]];i<=r;i++) num[b[i]]++;
            while (top) {
                ans=max(ans,(LL)a[st[top]].w*num[st[top]]);
                num[st[top--]]=0;
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

  

时间: 2024-10-15 10:39:28

【bzoj4241】 历史研究的相关文章

BZOJ4241 历史研究 莫队算法 堆

欢迎访问~原文出处--博客园-zhouzhendong&AK 去博客园看该题解 题目 Description IOI国历史研究的第一人--JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 1.

[bzoj4241][历史研究] (分块)

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

bzoj4241 历史研究

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4241 [题解] 和作诗相似. f[i,j]表示块i到块j的答案. g[i,j]表示1...i块中j出现次数. 那么分块直接做即可. 复杂度O(n根号n) 跑的好慢啊.. # include <vector> # include <stdio.h> # include <string.h> # include <algorithm> // # inclu

bzoj4241 历史研究——分块

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4241 就是分块,预处理出从第 i 块到 j 位置的答案,以及从第 i 块到最后位置间每个数出现的次数: 然后块内统计.块外暴力即可. 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using na

bzoj 4241 历史研究

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

【AT1219】历史研究

题面 题目描述 \(IOI\)国历史研究的第一人--\(JOI\)教授,最近获得了一份被认为是古代\(IOI\)国的住民写下的日记.\(JOI\)教授为了通过这份日记来研究古代\(IOI\)国的生活,开始着手调查日记中记载的事件. 日记中记录了连续\(N\)天发生的时间,大约每天发生一件. 事件有种类之分.第\(i\)天\((1<=i<=N)\)发生的事件的种类用一个整数\(X_i\)表示,\(X_i\)越大,事件的规模就越大. \(JOI\)教授决定用如下的方法分析这些日记: 选择日记中连续

习题:历史研究(回滚莫队)

题目 传送门 思路 很版的一道回滚莫队的题 我们如果用普通的莫队,我们发现最难维护的是最大值, 因为你无法预测缩减时最大值的变化,还要带一个线段树或者什么来维护 时间复杂度为\(O(n*log_n*\sqrt n)\) 但是我们想,我们如果已知一个莫队的左端点和右端点以及它的最大值 那么这个莫队向外拓展我们是很容易维护的 之后如果下一个操作也是向外拓展就向外拓展,如果是内缩, 我们就将这个莫队还原成为我们最开始已知的样子,在进行拓展 这也就是回滚莫队的主要思想,这道题也是如此 时间复杂度依然也是

AT1219 歴史の研究(回滚莫队)

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

使用TCP时序图解释BBR拥塞控制算法的几个细节

周六,由于要赶一个月底的Deadline,因此选择了在家VPN加班,大半夜就爬起来跑用例,抓数据...自然也就没有时间写文章和外出耍了...不过利用周日的午夜时间(不要问我为什么可以连续24小时不睡觉,因为我觉得吃饭睡觉是负担),我决定把工作上的事情先放下,还是要把每周至少一文补上,这已经成了习惯.由于上周实在太忙乱,所以自然根本没有更多的时间去思考一些"与工作无关且深入"的东西,我指的与工作无关并非意味着与IT,与互联网无关,只是意味着不是目前我在做的.比如在两年前,VPN,PKI这