只增莫队学习_4241历史研究_JOI 2013~2014 春季training合宿 竞技1 By PoPoQQQ

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

当莫队删数困难时,考虑“只增莫队”

 增数处理很容易,删数处理却较难处理。
 对于[L, R]区间的询问:假设L所处块的右端点为Rs,从Rs向两边增数!
 当L所处块相同时,R是只增的。L的变化肯定也是从Rs增数过来的。
 你可能会说,虽然说L在同一块内,但是他们大小是没有规律的。没错,所以在一次询问完成后,要把L还原到Rs,这样下一次还会是增数处理。

复杂度玄学: O ( 1 e 5 能 过 ) O(1e5能过) O(1e5)

AC代码

8002ms

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6+7;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
int n,m;
LL ans;
int ar[N],belong[N],br[N],now[N];
LL Ans[N];
int cnt[N],in_left[N];
struct lp{
    int l,r,id;
}cw[N];
bool cmp(const lp &a,const lp &b){
    if(belong[a.l]==belong[b.l]){
        return a.r<b.r;
    }
    return belong[a.l]<belong[b.l];
}
void update_left(int x,int f){
    cnt[now[x]]++;
    if((cnt[now[x]])*1LL*ar[x]>ans){
        ans=(cnt[now[x]])*1LL*ar[x];
    }
}
int main(){
    scanf("%d%d",&n,&m);
    int block=(int)sqrt(n*1.0);
    for(int i=1;i<=n;++i){
        scanf("%d",&ar[i]);
        br[i]=ar[i];
        belong[i]=(i-1)/block+1;
    }
    sort(br+1,br+1+n);
    int k=1;
    for(int i=2;i<=n;++i){
        if(br[i]!=br[i-1]){
            br[++k]=br[i];
        }
    }
    for(int i=1;i<=n;++i){
        now[i]=lower_bound(br+1,br+1+n,ar[i])-br;
    }
    for(int i=1;i<=m;++i){
        scanf("%d%d",&cw[i].l,&cw[i].r);
        cw[i].id=i;
    }
    memset(cnt,0,sizeof(cnt));
    sort(cw+1,cw+m+1,cmp);
    int L=1,R=0,limit;
    LL last_ans=0;
    ans=0;cw[0].l=0;belong[0]=0;
    for(int i=1;i<=m;++i){
        if(belong[cw[i-1].l]!=belong[cw[i].l]){
            memset(cnt,0,sizeof(cnt));
            limit=belong[cw[i].l]*block;
            R=limit;
            ans=last_ans=0;
        }
        L=min(limit+1,cw[i].r+1);
        while(R<cw[i].r){
            R++;ans=max((++cnt[now[R]])*1LL*ar[R],ans);
        }
        last_ans=ans;
        while(L>cw[i].l)update_left(--L,1);
        Ans[cw[i].id]=ans;
        for(int j=L;j<=cw[i].r&&j<=limit;++j){
            cnt[now[j]]--;
        }
        ans=last_ans;
    }
    for(int i=1;i<=m;++i){
        printf("%lld\n",Ans[i] );
    }
    return 0;
}

题目

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)

Source
JOI 2013~2014 春季training合宿 竞技1 By PoPoQQQ


以前写过的代码1:9479ms

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6+7;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
int n,m,ar[N],belong[N],br[N],now[N];
LL ans,Ans[N];
int cnt[N],in_left[N];
struct lp{
    int l,r,id;
}cw[N];
bool cmp(const lp &a,const lp &b){
    if(belong[a.l]==belong[b.l]){
        return a.r<b.r;
    }
    return belong[a.l]<belong[b.l];
}
void update_left(int x,int f){
	in_left[now[x]]++;
	if((in_left[now[x]]+cnt[now[x]])*1LL*ar[x]>ans){
		ans=(in_left[now[x]]+cnt[now[x]])*1LL*ar[x];
	}
}
int main(){
	scanf("%d%d",&n,&m);
		int sz=(int)sqrt(n*1.0);
		for(int i=1;i<=n;++i){
			scanf("%d",&ar[i]);
			br[i]=ar[i];
			belong[i]=(i-1)/sz+1;
		}
		sort(br+1,br+1+n);
		int k=1;
		for(int i=2;i<=n;++i){
			if(br[i]!=br[i-1]){
				br[++k]=br[i];
			}
		}
		for(int i=1;i<=n;++i){
			now[i]=lower_bound(br+1,br+1+n,ar[i])-br;
		}
		for(int i=1;i<=m;++i){
			scanf("%d%d",&cw[i].l,&cw[i].r);
			cw[i].id=i;
		}
		sort(cw+1,cw+m+1,cmp);
		int L=1,R=0,tmd;
		LL tmp=0;
		ans=0;cw[0].l=0;belong[0]=0;
		for(int i=1;i<=m;++i){
			if(belong[cw[i-1].l]!=belong[cw[i].l]){
				memset(cnt,0,sizeof(cnt));
				tmd=belong[cw[i].l]*sz;
				R=tmd;
				ans=tmp=0;
			}
			L=min(tmd+1,cw[i].r+1);
			while(L>cw[i].l)update_left(--L,1);
			while(R<cw[i].r){
				R++;
				tmp=max((++cnt[now[R]])*1LL*ar[R],tmp);
				ans=max(ans,(cnt[now[R]]+in_left[now[R]])*1LL*ar[R]);
			}
			Ans[cw[i].id]=ans;
			for(int j=L;j<=cw[i].r&&j<=tmd;++j){
				in_left[now[j]]--;
			}
			ans=tmp;
		}
		for(int i=1;i<=m;++i){
			printf("%lld\n",Ans[i] );
		}
	return 0;
}

以前写过的代码2: 10946ms

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6+7;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
int n,m,cnt[N],ar[N],br[N],now[N],belong[N],cnt_L,cnt_R;
LL ans,tmp_ans,Ans[N];
struct lp{
    int l,r,id;
}cw[N],liu[N];
bool cmp(const lp &a,const lp &b){
    if(belong[a.l]==belong[b.l]){
        return a.r<b.r;
    }
    return belong[a.l]<belong[b.l];
}
bool cmp2(const lp &a,const lp &b){
	if(a.l!=b.l)return a.l<b.l;
	return a.r<b.r;
}
void update(int x,int f){
	cnt[now[x]]+=f;
	cnt_L=min(cnt_L,now[x]);
	cnt_R=max(cnt_R,now[x]);
	if(ans<(cnt[now[x]]*1LL*ar[x])&&f==1){
		ans=(cnt[now[x]]*1LL*ar[x]);
	}
}
int main(){
	scanf("%d%d",&n,&m);
		int sz=(int)sqrt(n*1.0);
		for(int i=1;i<=n;++i){
			scanf("%d",&ar[i]);
			br[i]=ar[i];
			belong[i]=(i-1)/sz+1;
		}
		sort(br+1,br+1+n);
		int k=1;
		for(int i=2;i<=n;++i){
			if(br[i]!=br[i-1]){
				br[++k]=br[i];
			}
		}
		for(int i=1;i<=n;++i){
			now[i]=lower_bound(br+1,br+1+k,ar[i])-br;
		}
		for(int i=1;i<=m;++i){
			scanf("%d%d",&cw[i].l,&cw[i].r);
			cw[i].id=i;
		}
		sort(cw+1,cw+m+1,cmp);
		int L=0,R,liu_k=0;
		cnt_L=0,cnt_R=0;
		ans=tmp_ans=0;cw[0].l=liu[0].l=0;
		memset(cnt,0,sizeof(cnt));
		for(int i=1;i<=m;++i){
			if(belong[cw[i].l]==belong[cw[i].r]){
				liu[++liu_k].l=cw[i].l;
				liu[liu_k].r=cw[i].r;
				liu[liu_k].id=cw[i].id;
				continue;
			}
			if(belong[cw[i].l]!=belong[L]){
				cnt_L=max(cnt_L-5,0);cnt_R+=5;
				for(int j=cnt_L;j<=cnt_R;++j)cnt[j]=0;
				ans=0;R=cw[i].r;L=belong[cw[i].l]*sz;
				cnt_L=INF,cnt_R=0;
				for(int j=L;j<=R;++j)update(j,1);
				tmp_ans=ans;
			}
			ans=tmp_ans;L=belong[cw[i].l]*sz;
			while(R<cw[i].r)update(++R,1);
			tmp_ans=ans;
			while(L>cw[i].l)update(--L,1);
			Ans[cw[i].id]=ans;
			for(int j=L;j<belong[cw[i].l]*sz;++j)update(j,-1);
		}
		sort(liu+1,liu+liu_k+1,cmp2);
		L=0;
		for(int i=1;i<=liu_k;++i){
			if(liu[i].l!=L){
				ans=0;
				for(int j=0;j<=k;++j)cnt[j]=0;
				L=liu[i].l,R=liu[i].r;
				for(int j=L;j<=R;++j)update(j,1);
			}else{
				for(int j=R+1;j<=liu[i].r;++j){
					update(j,1);
				}
				R=liu[i].r;
			}
			Ans[liu[i].id]=ans;
		}
		for(int i=1;i<=m;++i){
			printf("%lld\n",Ans[i] );
		}
	return 0;
}

bzoj4241

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值