1. 程式人生 > >【CF840D】Destiny 分治(線段樹)

【CF840D】Destiny 分治(線段樹)

def bool open != ostream for get cto 都在

【CF840D】Destiny

題意:給你一個長度為n的序列,q次詢問,每次指定l r k,求[l,r]中出現次數$>\frac {r-l+1} k$的所有數中最小的那個數。

$n,q\le 3\times 10^5,a_i\le n,2\le k \le 5$

題解:考慮分治。對於每次詢問,我們將整個序列分成[1,mid]和(mid,n]兩部分,要麽詢問段與mid,mid+1有交點,要麽詢問段完全位於兩邊的某一段中,這種情況我們可以遞歸下去處理。

有一個顯然的結論,就是我們將一個區間任意分成兩部分,則出現次數最多的k個數要麽是左面出現次數最多的k個數,要麽是右面出現次數最多的k個數。

我們可以先預處理出:對於每個可能被分成的區間(其實就是線段樹上的節點),它的mid往左延伸一些長度,對應的區間中出現次數最多個k個數。即區間[l,mid],[l+1,mid],[l+2,mid]...[mid,mid]的答案。這個從右往左掃一遍很容易得出。再預處理出[mid+1,mid+1],[mid+1,mid+2]...[mid+1,r]的答案。詢問時直接拿出這兩段區間,然後把2k個數都拿出來,全都在vector上二分一下統計真實的出現次數即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=300010;
int n,m,now,ans;
int cnt[maxn],tim[maxn],val[maxn];
struct node
{
	int v[5];
	int & operator [] (const int &a) {return v[a];}
}t;
vector<node> ls[maxn<<2],rs[maxn<<2];
vector<int> p[maxn];
bool cmp(const int &a,const int &b) {return (cnt[a]==cnt[b])?(a<b):(cnt[a]>cnt[b]);}
void build(int l,int r,int x)
{
	int i,j,mid=(l+r)>>1;
	now++;
	memset(t.v,0,sizeof(t.v));
	for(i=mid;i>=l;i--)
	{
		if(tim[val[i]]!=now)	tim[val[i]]=now,cnt[val[i]]=0;
		cnt[val[i]]++;
		for(j=0;j<5;j++)	if(t[j]==val[i])	break;
		if(j==5&&cmp(val[i],t[4]))	t[4]=val[i];
		for(j=4;j>0;j--)	if(cmp(t[j],t[j-1]))	swap(t[j-1],t[j]);
		ls[x].push_back(t);
	}
	now++;
	memset(t.v,0,sizeof(t.v));
	for(i=mid+1;i<=r;i++)
	{
		if(tim[val[i]]!=now)	tim[val[i]]=now,cnt[val[i]]=0;
		cnt[val[i]]++;
		for(j=0;j<5;j++)	if(t[j]==val[i])	break;
		if(j==5&&cmp(val[i],t[4]))	t[4]=val[i];
		for(j=4;j>0;j--)	if(cmp(t[j],t[j-1]))	swap(t[j-1],t[j]);
		rs[x].push_back(t);
	}
	if(l==r)	return ;
	build(l,mid,lson),build(mid+1,r,rson);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	//freopen("cf840D.in","r",stdin);
	n=rd(),m=rd();
	int i,j,a,b,c,l,r,mid,x;
	for(i=1;i<=n;i++)	val[i]=rd(),p[val[i]].push_back(i);
	build(1,n,1);
	for(i=1;i<=m;i++)
	{
		a=rd(),b=rd(),c=rd(),ans=1<<30;
		l=1,r=n,x=1;
		while(1)
		{
			mid=(l+r)>>1;
			if(a<=mid+1&&b>=mid)
			{
				now++;
				if(a<=mid)
				{
					t=ls[x][mid-a];
					for(j=0;j<c;j++)
					{
						if(tim[t[j]]!=now)	tim[t[j]]=now,cnt[t[j]]=0;
						if(c*(upper_bound(p[t[j]].begin(),p[t[j]].end(),b)-lower_bound(p[t[j]].begin(),p[t[j]].end(),a))>b-a+1)	ans=min(ans,t[j]);
					}
				}
				if(b>mid)
				{
					t=rs[x][b-mid-1];
					for(j=0;j<c;j++)
					{
						if(tim[t[j]]!=now)	tim[t[j]]=now,cnt[t[j]]=0;
						if(c*(upper_bound(p[t[j]].begin(),p[t[j]].end(),b)-lower_bound(p[t[j]].begin(),p[t[j]].end(),a))>b-a+1)	ans=min(ans,t[j]);
					}
				}
				break;
			}
			if(b<mid)	r=mid,x=lson;
			else	l=mid+1,x=rson;
		}
		if(ans==1<<30)	puts("-1");
		else	printf("%d\n",ans);
	}
	return 0;
}//5 3 1 2 1 3 2 2 5 3 1 2 3 5 5 2

【CF840D】Destiny 分治(線段樹)