D. Cut and Stick

D. Cut and Stick
time limit per test3 seconds
memory limit per test512 megabytes
inputstandard input
outputstandard output
Baby Ehab has a piece of Cut and Stick with an array a of length n written on it. He plans to grab a pair of scissors and do the following to it:

pick a range (l,r) and cut out every element al, al+1, …, ar in this range;
stick some of the elements together in the same order they were in the array;
end up with multiple pieces, where every piece contains some of the elements and every element belongs to some piece.
More formally, he partitions the sequence al, al+1, …, ar into subsequences. He thinks a partitioning is beautiful if for every piece (subsequence) it holds that, if it has length x, then no value occurs strictly more than ⌈x2⌉ times in it.

He didn’t pick a range yet, so he’s wondering: for q ranges (l,r), what is the minimum number of pieces he needs to partition the elements al, al+1, …, ar into so that the partitioning is beautiful.

A sequence b is a subsequence of an array a if b can be obtained from a by deleting some (possibly zero) elements. Note that it does not have to be contiguous.

Input
The first line contains two integers n and q (1≤n,q≤3⋅105) — the length of the array a and the number of queries.

The second line contains n integers a1, a2, …, an (1≤ai≤n) — the elements of the array a.

Each of the next q lines contains two integers l and r (1≤l≤r≤n) — the range of this query.

Output
For each query, print the minimum number of subsequences you need to partition this range into so that the partitioning is beautiful. We can prove such partitioning always exists.

Example
inputCopy
6 2
1 3 2 3 3 2
1 6
2 5
outputCopy
1
2
Note
In the first query, you can just put the whole array in one subsequence, since its length is 6, and no value occurs more than 3 times in it.

In the second query, the elements of the query range are [3,2,3,3]. You can’t put them all in one subsequence, since its length is 4, and 3 occurs more than 2 times. However, you can partition it into two subsequences: [3] and [2,3,3].

思考:
充分利用规律:若有影响则大于1/2,可大大简化

方法一:线段树(或倍增dp)

#include <bits/stdc++.h>
using namespace std;
int a[300005],tree[1200005];
vector<int> v[300005];
int cnt(int l,int r,int c) {
    return upper_bound(v[c].begin(),v[c].end(),r)-lower_bound(v[c].begin(),v[c].end(),l);
}
void build(int node,int st,int en) {
    if (st==en) tree[node]=a[st];
    else {
        int mid=(st+en)/2;
        build(2*node,st,mid);
        build(2*node+1,mid+1,en);
        tree[node]=(cnt(st,en,tree[2*node])>cnt(st,en,tree[2*node+1])? tree[2*node]:tree[2*node+1]);
    }
}
int query(int node,int st,int en,int l,int r) {
    if (en<l || st>r || r<l)
    return 0;
    if (l<=st && en<=r)
    return cnt(l,r,tree[node]);
    int mid=(st+en)/2;
    return max(query(2*node,st,mid,l,r),query(2*node+1,mid+1,en,l,r));
}
int main() {
    int n,q;
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        v[a[i]].push_back(i);
    }
    build(1,1,n);
    while (q--) {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",max(1,2*query(1,1,n,l,r)-(r-l+1)));
    }
}


我写的:
1.一个重大错误!!!:
//递归不要用全局变量!!!会改变数值的!!(用小数据调一下也快些)
2.//ask的范围不影响时间的
3.//线段树查询要点:递归看,若在区间内则直接有,若在区间外则递归到下层
4.证明://思想:如果在这些区间都不是众数,那总共也不可能超过1/2

//#pragma GCC optimize(3,"Ofast","inline")
//#pragma GCC optimize(2)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<iostream>
#include<algorithm>
//#include<string>
//#include<sstream>
#include<vector>
#include<map>
//#include<set>
//#include<ctype.h>
//#include<stack>
//#include<queue>
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
//FILE*fp=freopen("text.out","w",stdout);
#endif
using namespace std;
//#define ll long long
#define ld long double
#define pii pair<int,int>
#define piii pair<int,pii>
#define pll pair<ll,ll>
#define plll pair<ll,pll> 
#define pdd pair<double,double>
#define pdi pair<double,int>
#define pid pair<int,double>
#define vi vector <int> 
#define vii vector <vi> 
#define vl vector<ll>
#define st first
#define nd second
#define pb push_back
#define mp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
#define _forplus(i,a,b) for( register int i=(a); i<=(b); i++)
#define forplus(i,a,b) for( register int i=(a); i<(b); i++)
#define _forsub(i,a,b) for( register int i=(a); i>=(b); i--)
#define _forauto(a,b) for(auto &(a):(b))
#define _forautome(a,b,c) for(auto (a) = (b); (a) != (c); (a)++)
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
//参考:set<int>::iterator iter = vis.begin();
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define pi (acos(-1))
#define EPS 0.00000001
#define MOD 1000000007
#define fastio 	std::ios::sync_with_stdio(false);std::cin.tie(0);
//#define int ll
#define N 300005
#define ask(k,l,r) (int)(upper_bound(ALL(v[k]),r)-lower_bound(ALL(v[k]),l))
int n,q,a[N],l,r,ans,tree[N*4];//递归不要用全局变量!!!会改变数值的!!(用小数据调一下也快些) 
vector<int>v[N];
void build(int i,int l,int r){
	if(l==r){
		tree[i]=a[l];
		return;
	}
	int m=(l+r)/2;
	build(2*i,l,m);
	build(2*i+1,m+1,r);
	//ask的范围不影响时间的 
	tree[i]=(ask(tree[2*i],l,r)>ask(tree[2*i+1],l,r)?tree[2*i]:tree[2*i+1]);
}
int query(int now,int ll,int rr,int l,int r){
	if(ll>=l&&rr<=r){
		return ask(tree[now],l,r);
	}//线段树要点:递归看,若在区间内则直接有,若在区间外则递归到下层 
	if(ll>r||rr<l)return 0;
	int m=(ll+rr)/2;
	return max(query(now*2,ll,m,l,r),query(now*2+1,m+1,rr,l,r));
}
//思想:如果在这些区间都不是众数,那总共也不可能超过1/2 
int32_t main(){
	fastio
	cin>>n>>q;
	_forplus(i,1,n){
		cin>>a[i];
		v[a[i]].pb(i);
	}
	build(1,1,n); 
	_forplus(i,1,q){
		cin>>l>>r;
		//寻找众数个数 
		cout<<max(1,2*query(1,1,n,l,r)-(r-l+1))<<endl;
	}
	return 0;
}

方法二:众数多于1/2才有效,可随机化

#include<bits/stdc++.h>
using namespace std;
int n,q,l,r,a[300005];
vector<int> v[300005];
#define all(v) (v).begin(),(v).end()
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
#endif
int main() {
	srand(time(NULL));
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) {
		scanf("%d",a+i);
		v[a[i]].push_back(i);
	}
	for(int i=1;i<=q;i++) {
		scanf("%d%d",&l,&r);
		int ans=0;//众数数目 
		for(int j=1;j<=40;j++) {//次数多,概率大,因为若有关,必过半, 
			int id=a[rand()*rand()%(r-l+1)+l];//利用了众数更容易被取到。两个rand()更随机 
			ans=max(ans,int(upper_bound(all(v[id]),r)-lower_bound(all(v[id]),l)));
		}
		cout<<max(1,ans+ans-(r-l+1))<<endl;
	}
	return 0;
}

一般方法:区间分块求众数
陈立杰的区间众数解题报告,不仅不必过半还可修改

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值