蒲公英(历史性的一刻)

题目描述
亲爱的哥哥:

你在那个城市里面过得好吗?

我在家里面最近很开心呢。昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了。我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢。不过奶奶说他是很难受的时候才做出这样的事的……

最近村子里长出了一大片一大片的蒲公英。一刮风,这些蒲公英就能飘到好远的地方了呢。我觉得要是它们能飘到那个城市里面,让哥哥看看就好了呢!

哥哥你要快点回来哦!

爱你的妹妹 Violet

Azure 读完这封信之后微笑了一下。

“蒲公英吗……”

在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。

为了简化起见,我们把所有的蒲公英看成一个长度为 n 的序列 (),其中 为一个正整数,表示第 i 棵蒲公英的种类编号。

而每次询问一个区间[l, r],你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。

注意,你的算法必须是在线的。

输入格式
第一行两个整数 n, m,表示有 n 株蒲公英,m 次询问。

接下来一行 n 个空格分隔的整数 ,表示蒲公英的种类。

再接下来 m 行每行两个整数 ,我们令上次询问的结果为 x(如果这是第一次询问,则 x = 0)。

令 ,如果 l > r,则交换 l, r。

最终的询问区间为[l, r]。

输出格式
输出 m 行。每行一个整数,表示每次询问的结果。

样例
样例输入
6 3
1 2 3 2 1 2
1 5
3 6
1 5
样例输出
1
2
1

很简单的一个分块思路,拿unique和map离散化,再用分块
但是是T的

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
map<int,int> push;
map<int,int> back;
int n,m;
int len;
int from,to;
ll t[60000];
ll a[60000];
int f[500][60000];
int l[60000],r[60000];
int vis[60000];
ll num[60000];
void div(){
	int sq=sqrt(n);
	for(int i=1;i<=sq;i++){
		l[i]=sq*(i-1)+1;
		r[i]=sq*i;
	}
	if(r[sq]<n){
		sq++;
		l[sq]=r[sq-1]+1;
		r[sq]=n;
	}
	for(int i=1;i<=sq;i++){
		for(int j=l[i];j<=r[i];j++){
			vis[j]=i;
			f[i][push[a[j]]]++;
		}
	}
}
int check(int x,int y){
	memset(num,0,sizeof(num));
	int ans=0;ll last=0x7ffffffff;
	int p=vis[x],q=vis[y];
	if(p==q){
		for(int i=x;i<=y;i++){
			num[push[a[i]]]++;
		}
		for(int i=x;i<=y;i++){
			if((ans<num[push[a[i]]])||(ans==num[push[a[i]]]&&last>a[i])){
				ans=num[push[a[i]]];
				last=a[i];
			}
		}
	}
	else{
		for(int i=x;i<=r[p];i++){
			num[push[a[i]]]++;
		}
		for(int i=p+1;i<q;i++){
			for(int j=1;j<=len;j++){
				num[j]+=f[i][j];
			}
		}
		for(int i=l[q];i<=y;i++){
			num[push[a[i]]]++;
		}
		for(int i=1;i<=len;i++){
			if((ans<num[i])||(ans==num[i]&&last>back[i])){
				ans=num[i];
				last=back[i];
			}
		}
	}
	return last;
}
int main(){
	int x=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];t[i]=a[i];
	}
	sort(t+1,t+1+n);
	len=unique(t+1,t+1+n)-t-1;
	for(int i=1;i<=len;i++){
		push[t[i]]=i;
		back[i]=t[i];
	}
	div();
	for(int i=1;i<=m;i++){
		cin>>from>>to;
		from=(from+x-1)%n+1;
		to=(to+x-1)%n+1; 
		if(from>to) swap(from,to);
		cout<<check(from,to)<<endl; 
		x=check(from,to);
	}
}
注意时间

image

开始正题,在pig的教导下,我成功的领悟到了初始化的方法
一开始是这样初始化的,很暴力的单点加和,再加上map的映射,导致复杂度起飞

点击查看代码
for(int i=1;i<=sq;i++){
for(int j=l[i];j<=r[i];j++){
	vis[j]=i;
	f[i][push[a[j]]]++;
	}
}
int maxx=0;int last=0;
for(int i=1;i<=sq;i++){
	for(int j=i;j<=sq;j++){
	for(int k=l[j];k<=r[j];k++){
		sum[push[a[k]]]++;
		if(sum[push[a[k]]]>maxx){
			maxx=sum[push[a[k]]];
			last=push[a[k]];
		}
		else if(maxx==sum[push[a[k]]]&&push[a[k]]<last){
			last=push[a[k]];
		}
	}
	ma[i][j]=last;ma1[i][j]=sum[last];
}
memset(sum,0,sizeof(sum));maxx=0;
后面按照题解,发现了一个优化的地方

image
对一个区间的查询,会有以上三种情况,分别来分析
首先,散块中出现的数一定需要更新,并于原来的答案比较
其次,散块和整块中出现的也要更新,这就需要预处理出每个区块中对应数出现的次数(这里的优化是前缀和,我没想到,pig教的),然后更新比较
然鹅,对于整块中出现的数,我们会发现,它们没有被更新,所以可以不用去遍历它们,但问题是,可能整块中有一个数,他在整块中出现的次数比本次更新的数都要大,因此,要去预处理出区块间的众数,并在最后进行比较
然后,就卡在这里了,因为卡在了对于记录次数数组sum的清零上,如果直接memset会t,如果加标记最后也要请标记,然后就陷入了死循环,况且,求众数也卡在了处理上,用的单点处理
最后还是参照了pig的代码参考了思路
然后发现,众数可以用前缀和,关于数组清零,则在加和时标记并清零,比较时再将标记还回去
求众数

for(int i=1;i<=sq;i++){
	for(int j=l[i];j<=r[i];j++) vis[j]=i;
	for(int j=1;j<=r[i];j++){
		f[i][a[j]]++;
	}
}
for(int i=1;i<=sq;i++){
	for(int j=i;j<=sq;j++){
		int d=maxn[i][j-1].now;
		for(int k=l[j];k<=r[j];k++){
//			cout<<f[j][a[k]]<<" "<<f[i-1][a[k]];
			if(((f[j][a[k]]-f[i-1][a[k]])>(f[j][d]-f[i-1][d]))||(f[j][a[k]]-f[i-1][a[k]]==f[j][d]-f[i-1][d]&&(b[a[k]]<b[d]))){
				d=a[k];
//				cout<<(f[j][d]-f[i-1][d])*b[d]<<" ";
			}
		}
	maxn[i][j].now=d;
	maxn[i][j].num=f[j][d]-f[i-1][d];
	}
}

添加标记

for(int i=from;i<=r[p];i++){
	if(used[a[i]]==0){
		num[a[i]]=0;
		num[a[i]]+=f[q-1][a[i]]-f[p][a[i]];
		used[a[i]]=1;//标记并加上整块的次数
	}
	num[a[i]]++;
//	cout<<num[a[i]]<<" "<<b[a[i]];
//	printf("\n");
}
for(int i=l[q];i<=to;i++){
	if(used[a[i]]==0){
		num[a[i]]=0;
		num[a[i]]+=f[q-1][a[i]]-f[p][a[i]];
		used[a[i]]=1;//标记并加上整块的次数 
	}
	num[a[i]]++;
//	cout<<num[a[i]]<<" "<<b[a[i]];
//	printf("\n");
}
for(int i=from;i<=r[p];i++){
	if(used[a[i]]==1){
		if((ans<num[a[i]])||(ans==num[a[i]]&&(ans1>b[a[i]]))){
			ans1=b[a[i]];
			ans=num[a[i]];
		}
		used[a[i]]=0;//返还 
	}
		}
for(int i=l[q];i<=to;i++){
	if(used[a[i]]==1){
		if((ans<num[a[i]])||(ans==num[a[i]]&&(ans1>b[a[i]]))){
			ans1=b[a[i]];
			ans=num[a[i]];
		}
		used[a[i]]=0;//返还 
	}
}

然后就没了

点击查看代码
#include<bits/stdc++.h>
using namespace std;
map<long long,int> mp;
long long a[110000];
long long b[110000];
int l[100001],vis[100001],r[100001];
long long f[500][100001];
struct node{
	long long num;
	int now;
}maxn[500][500];
bool used[100001];
long long num[100001];
int n,m;
int cnt;
int from,to;
void div(){
	int sq=sqrt(n);
	for(int i=1;i<=sq;i++){
		l[i]=sq*(i-1)+1;
		r[i]=sq*i;
	}
	r[sq]=n;
	for(int i=1;i<=sq;i++){
		for(int j=l[i];j<=r[i];j++) vis[j]=i;
		for(int j=1;j<=r[i];j++){
			f[i][a[j]]++;
		}
	}
	for(int i=1;i<=sq;i++){
		for(int j=i;j<=sq;j++){
			int d=maxn[i][j-1].now;
			for(int k=l[j];k<=r[j];k++){
//				cout<<f[j][a[k]]<<" "<<f[i-1][a[k]];
				if(((f[j][a[k]]-f[i-1][a[k]])>(f[j][d]-f[i-1][d]))||(f[j][a[k]]-f[i-1][a[k]]==f[j][d]-f[i-1][d]&&(b[a[k]]<b[d]))){
					d=a[k];
//					cout<<(f[j][d]-f[i-1][d])*b[d]<<" ";
				}
			}
			maxn[i][j].now=d;
			maxn[i][j].num=f[j][d]-f[i-1][d];
		}
	}
}
long long check(int from,int to){
	long long ans=0;long long ans1=0;
	int p=vis[from],q=vis[to];
	if(p==q){
		for(int i=from;i<=to;i++){
			if(used[a[i]]==0){
				num[a[i]]=0;
				used[a[i]]=1;
			}
			num[a[i]]++;
			if((ans<num[a[i]])||(ans==num[a[i]]&&(ans1>b[a[i]]))){
				ans1=b[a[i]];
				ans=num[a[i]];
			}
		}
		for(int i=from;i<=to;i++){
			if(used[a[i]]==1){
				used[a[i]]=0;
			}
		}
	}
	else{
		int d=maxn[p+1][q-1].now;
		int e=maxn[p+1][q-1].num;
//		cout<<e<<" "<<b[d];
//		printf("\n");
		for(int i=from;i<=r[p];i++){
			if(used[a[i]]==0){
				num[a[i]]=0;
				num[a[i]]+=f[q-1][a[i]]-f[p][a[i]];
				used[a[i]]=1;//标记并加上整块的次数
			}
			num[a[i]]++;
//			cout<<num[a[i]]<<" "<<b[a[i]];
//			printf("\n");
		}
		for(int i=l[q];i<=to;i++){
			if(used[a[i]]==0){
				num[a[i]]=0;
				num[a[i]]+=f[q-1][a[i]]-f[p][a[i]];
				used[a[i]]=1;//标记并加上整块的次数 
			}
			num[a[i]]++;
//			cout<<num[a[i]]<<" "<<b[a[i]];
//			printf("\n");
		}
		for(int i=from;i<=r[p];i++){
			if(used[a[i]]==1){
				if((ans<num[a[i]])||(ans==num[a[i]]&&(ans1>b[a[i]]))){
				ans1=b[a[i]];
				ans=num[a[i]];
			}
				used[a[i]]=0;//返还 
			}
		}
		for(int i=l[q];i<=to;i++){
			if(used[a[i]]==1){
				if((ans<num[a[i]])||(ans==num[a[i]]&&(ans1>b[a[i]]))){
				ans1=b[a[i]];
				ans=num[a[i]];
			}
				used[a[i]]=0;//返还 
			}
		}
		if((ans<e)||(ans==e&&(ans1>b[d]))){
			ans1=b[d];
		}
	}
	return ans1;
}
int main(){
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) 
	{
		if(mp[a[i]]==0){
			cnt++;
			mp[a[i]]=cnt;
			b[cnt]=a[i];
		}
		a[i]=mp[a[i]];
	}
	div();
	int x=0;
	for(int i=1;i<=m;i++){
		cin>>from>>to;
		from=(from+x-1)%n+1;
		to=(to+x-1)%n+1;
		if(from>to) swap(from,to);
		x=check(from,to);
		cout<<x<<endl;
		
	}
}

附带上历史的研究

点击查看代码
#include<bits/stdc++.h>
using namespace std;
map<long long,int> mp;
long long a[110000];
long long b[110000];
int l[100001],vis[100001],r[100001];
long long f[500][100001];
struct node{
	long long num;
	int now;
}maxn[500][500];
bool used[100001];
long long num[100001];
int n,m;
int cnt;
int from,to;
void div(){
	int sq=sqrt(n);
	for(int i=1;i<=sq;i++){
		l[i]=sq*(i-1)+1;
		r[i]=sq*i;
	}
	r[sq]=n;
	for(int i=1;i<=sq;i++){
		for(int j=l[i];j<=r[i];j++) vis[j]=i;
		for(int j=1;j<=r[i];j++){
			f[i][a[j]]++;
		}
	}
	for(int i=1;i<=sq;i++){
		for(int j=i;j<=sq;j++){
			int d=maxn[i][j-1].now;
			for(int k=l[j];k<=r[j];k++){
//				cout<<f[j][a[k]]<<" "<<f[i-1][a[k]];
				if(((f[j][a[k]]-f[i-1][a[k]])*b[a[k]])>((f[j][d]-f[i-1][d])*b[d])){
					d=a[k];
//					cout<<(f[j][d]-f[i-1][d])*b[d]<<" ";
				}
			}
			maxn[i][j].now=d;
			maxn[i][j].num=f[j][d]-f[i-1][d];
		}
	}
}
long long check(int from,int to){
	long long ans=0;
	int p=vis[from],q=vis[to];
	if(p==q){
		for(int i=from;i<=to;i++){
			if(used[a[i]]==0){
				num[a[i]]=0;
				used[a[i]]=1;
			}
			num[a[i]]++;
			ans=max(ans,(long long)num[a[i]]*b[a[i]]);
		}
		for(int i=from;i<=to;i++){
			if(used[a[i]]==1){
				used[a[i]]=0;
			}
		}
	}
	else{
		int d=maxn[p+1][q-1].now;
		int e=maxn[p+1][q-1].num;
//		cout<<e<<" "<<b[d];
//		printf("\n");
		for(int i=from;i<=r[p];i++){
			if(used[a[i]]==0){
				num[a[i]]=0;
				num[a[i]]+=f[q-1][a[i]]-f[p][a[i]];
				used[a[i]]=1;
			}
			num[a[i]]++;
//			cout<<num[a[i]]<<" "<<b[a[i]];
//			printf("\n");
		}
		for(int i=l[q];i<=to;i++){
			if(used[a[i]]==0){
				num[a[i]]=0;
				num[a[i]]+=f[q-1][a[i]]-f[p][a[i]];
				used[a[i]]=1;
			}
			num[a[i]]++;
//			cout<<num[a[i]]<<" "<<b[a[i]];
//			printf("\n");
		}
		for(int i=from;i<=r[p];i++){
			if(used[a[i]]==1){
				ans=max(ans,(long long)num[a[i]]*b[a[i]]);
				used[a[i]]=0;
			}
		}
		for(int i=l[q];i<=to;i++){
			if(used[a[i]]==1){
				ans=max(ans,(long long)num[a[i]]*b[a[i]]);
				used[a[i]]=0;
			}
		}
		ans=max(ans,(long long)b[d]*e);
	}
	return ans;
}
int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) 
	{
		if(mp[a[i]]==0){
			cnt++;
			mp[a[i]]=cnt;
			b[cnt]=a[i];
		}
		a[i]=mp[a[i]];
	}
	div();
	for(int i=1;i<=m;i++){
		cin>>from>>to;
		cout<<check(from,to)<<endl;
	}
}
posted @ 2024-04-30 21:22  shaoyufei  阅读(4)  评论(0编辑  收藏  举报