首页 > 其他分享 >LOJ 分块入门 1~9

LOJ 分块入门 1~9

时间:2022-10-13 17:02:19浏览次数:82  
标签:10 入门 分块 LOJ void write int flag getchar

LOJ 数列分块 \(1\sim 9\)

T1

区间加,单点查。

没啥好说的,分块都不想写。树状数组 + 差分解决。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
#define lowbit(_) (_&-_)
const int _SIZE=5e4;
int n,sum[_SIZE+5];
void update(int x,int v) {for (;x<=n;x+=lowbit(x)) sum[x]+=v;}
int query(int x)
{
	int res=0;
	for (;x;x-=lowbit(x)) res+=sum[x];
	return res;
}
int a[_SIZE+5],diff[_SIZE+5];
signed main()
{
	read(n);
	for (int i=1,temp;i<=n;i++) read(a[i]),diff[i]=a[i]-a[i-1];
	for (int i=1;i<=n;i++) update(i,diff[i]);
	for (int i=1;i<=n;i++)
	{
		int opt,l,r,c;read(opt),read(l),read(r),read(c);
		if (opt==0) update(l,c),update(r+1,-c);
		else writewith(query(r),'\n');
	}
	return 0;
}

T2

区间加,区间查比 \(k\) 小的数。

先将原数列分块,对每一个块开一个 vector,将数列元素存入 vector,并将 vector 排序,对于询问,在散块暴力统计,整块直接在 vector 中二分即可。

对于修改操作,整块直接打标记即可,散块暴力修改,然后暴力重构散块的 vector 并排序。

总时间复杂度 \(\mathcal O(n\sqrt n\log n)\)。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=5e4,_BLOCK=1e3;
int pos[_BLOCK+5],bl[_BLOCK+5],br[_BLOCK+5],tag[_BLOCK+5];
int a[_SIZE+5],cnt,len,n;
vector<int> block[_BLOCK+5];
void init()
{
	len=sqrt(n),cnt=(n-1)/len+1;
	for (int i=1;i<=cnt;i++)
		bl[i]=(i-1)*len+1,br[i]=min(n,i*len);
	for (int i=1;i<=cnt;i++)
	{
		for (int j=bl[i];j<=br[i];j++)
		{
			pos[j]=i;
			block[i].push_back(a[j]);
		}
		sort(block[i].begin(),block[i].end());
	}
}
void rebuild(int id)
{
	vector<int>().swap(block[id]);
	for (int i=bl[id];i<=br[id];i++) block[id].push_back(a[i]);
	sort(block[id].begin(),block[id].end());
}
void add(int l,int r,int c)
{
	if (pos[l]==pos[r])
	{
		for (int i=l;i<=r;i++) a[i]+=c;
		rebuild(pos[l]);
	}
	else
	{
		for (int i=l;i<=br[pos[l]];i++) a[i]+=c;
		rebuild(pos[l]);
		for (int i=pos[l]+1;i<pos[r];i++) tag[i]+=c;
		for (int i=bl[pos[r]];i<=r;i++) a[i]+=c;
		rebuild(pos[r]);
	}
}
int query(int l,int r,int c)
{
	int res=0;
	if (pos[l]==pos[r])
	{
		for (int i=l;i<=r;i++) res+=(a[i]+tag[pos[l]])<c;
		return res;
	}
	else
	{
		for (int i=l;i<=br[pos[l]];i++) res+=(a[i]+tag[pos[l]])<c;
		for (int i=pos[l]+1;i<pos[r];i++)
			res+=lower_bound(block[i].begin(),block[i].end(),c-tag[i])-block[i].begin();
		for (int i=bl[pos[r]];i<=r;i++) res+=(a[i]+tag[pos[r]])<c;
		return res;
	}
}
signed main()
{
	read(n);
	for (int i=1;i<=n;i++) read(a[i]);
	init();
	for (int i=1;i<=n;i++)
	{
		int opt,l,r,c;
		read(opt),read(l),read(r),read(c);
		if (opt==0) add(l,r,c);
		else writewith(query(l,r,c*c),'\n');
	}
	return 0;
}

T3

区间加,区间查询前驱。

和 \(2\) 的思路近乎相同,同样是用 vector 维护块内有序数列,然后散块暴力,整块二分即可。注意要判断块内无解的情况。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=1e5,_BLOCK=1e5;
int pos[_SIZE+5],bl[_BLOCK+5],br[_BLOCK+5],tag[_BLOCK+5];
vector<int> block[_BLOCK+5];
int a[_SIZE+5],n,len,cnt;
void init()
{
	len=sqrt(n),cnt=(n-1)/len+1;
	for (int i=1;i<=cnt;i++)
		bl[i]=(i-1)*len+1,br[i]=min(n,len*i);
	for (int i=1;i<=cnt;i++)
	{
		for (int j=bl[i];j<=br[i];j++)
		{
			pos[j]=i;
			block[i].push_back(a[j]);
		}
		sort(block[i].begin(),block[i].end());
	}
}
void rebuild(int id)
{
	block[id].clear();
	for (int i=bl[id];i<=br[id];i++) block[id].push_back(a[i]);
	sort(block[id].begin(),block[id].end());
}
void add(int l,int r,int c)
{
	
	if (pos[l]==pos[r])
	{
		for (int i=l;i<=r;i++) a[i]+=c;
		rebuild(pos[l]);
	}
	else
	{
		for (int i=l;i<=br[pos[l]];i++) a[i]+=c;
		for (int i=pos[l]+1;i<pos[r];i++) tag[i]+=c;
		for (int i=bl[pos[r]];i<=r;i++) a[i]+=c;
		rebuild(pos[r]);rebuild(pos[l]);
	}
	
}
int query(int l,int r,int c)
{
	int res=-1;
	if (pos[l]==pos[r])
	{
		for (int i=l;i<=r;i++) if (a[i]+tag[pos[l]]<c) res=max(a[i]+tag[pos[l]],res);
		return res;
	}
	else
	{
		for (int i=l;i<=br[pos[l]];i++)
			if (a[i]+tag[pos[l]]<c) res=max(res,a[i]+tag[pos[l]]);
		for (int i=pos[l]+1;i<pos[r];i++)
		{
			auto pos=lower_bound(block[i].begin(),block[i].end(),c-tag[i]);
			if (pos!=block[i].begin()) res=max(res,*(--pos)+tag[i]);
		}
		for (int i=bl[pos[r]];i<=r;i++)
			if (a[i]+tag[pos[r]]<c) res=max(res,a[i]+tag[pos[r]]);
		return res;
	}
}
signed main()
{
	double st=clock();
	read(n);
	for (int i=1;i<=n;i++) read(a[i]);
	init();
	for (int i=1;i<=n;i++)
	{
		int opt,l,r,c,ans;
		read(opt),read(l),read(r),read(c);
		if (opt==0) add(l,r,c);
		else writewith(query(l,r,c),'\n');
	}
	return 0;
}

T4

区间加,区间和。

分块经典题。散块暴力修改,整块打 tag 标记(类似标记永久化?)。询问的时候加上 tag 标记对答案的贡献即可。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
#define int long long
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=5e4;
int len,bl[405],br[405],cnt;
int pos[_SIZE+5];
int n,a[_SIZE+5];
int sum[405],tag[405];
void add(int l,int r,int c)
{
	if (pos[l]==pos[r])
	{
		sum[pos[l]]+=(r-l+1)*c;
		for (int i=l;i<=r;i++) a[i]+=c;
	}
	else
	{
		for (int i=l;i<=br[pos[l]];i++) a[i]+=c;
		sum[pos[l]]+=(br[pos[l]]-l+1)*c;
		for (int i=pos[l]+1;i<pos[r];i++) tag[i]+=c;
		for (int i=bl[pos[r]];i<=r;i++) a[i]+=c;
		sum[pos[r]]+=(r-bl[pos[r]]+1)*c;
	}
}
int query(int l,int r,int c)
{
	int res=0;
	if (pos[l]==pos[r])
	{
		for (int i=l;i<=r;i++) res+=a[i]+tag[pos[l]];
		return res%c;
	}
	else
	{
		for (int i=l;i<=br[pos[l]];i++) res+=a[i]+tag[pos[l]];
		for (int i=pos[l]+1;i<pos[r];i++) res+=sum[i]+tag[i]*(br[i]-bl[i]+1);
		for (int i=bl[pos[r]];i<=r;i++) res+=a[i]+tag[pos[r]];
		return res%c;
	}
}
signed main()
{
	read(n);
	for (int i=1;i<=n;i++) read(a[i]);
	len=sqrt(n),cnt=(n-1)/len+1;
	for (int i=1;i<=cnt;i++) bl[i]=(i-1)*len+1,br[i]=min(n,i*len);
	for (int i=1;i<=cnt;i++)
		for (int j=bl[i];j<=br[i];j++)
			pos[j]=i,sum[i]+=a[j];
	for (int i=1;i<=n;i++)
	{
		int opt,l,r,c;
		read(opt),read(l),read(r),read(c);
		if (opt==0) add(l,r,c);
		else writewith(query(l,r,c+1),'\n');
	}
	return 0;
}

T5

区间开方,区间求和。

区间开方有一个很好的性质,就是一个数开不了多少方就会变成 \(1\),而 \(1\) 和 \(0\) 无论怎么开方都是自己,不会发生改变。

根据这一性质我们可以对于开方操作暴力修改,然后如果一个块内的所有元素全部变成了 \(1\) 或 \(0\),就给这个块打一个 tag,下次再访问到这个块时就不进行修改操作了。

时间复杂度均摊下来应该还是 \(\mathcal O(n\sqrt n)\) 的吧。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=5e4,_BLOCK=1e3;
int pos[_SIZE+5],bl[_BLOCK+5],br[_BLOCK+5];
int sum[_BLOCK+5],tag[_BLOCK+5];
int a[_SIZE+5],cnt,len,n;
void init()
{
	len=sqrt(n),cnt=(n-1)/len+1;
	for (int i=1;i<=cnt;i++)
		bl[i]=(i-1)*len+1,br[i]=min(i*len,n);
	for (int i=1;i<=cnt;i++)
		for (int j=bl[i];j<=br[i];j++)
			pos[j]=i,sum[i]+=a[j];
}
void change(int id,int needChange)
{
	if (tag[id]) return;
	bool flag=1;sum[id]=0;
	for (int i=bl[id];i<=br[id];i++) 
	{
		if (needChange) a[i]=sqrt(a[i]);
		if (a[i]>1) flag=0;
		sum[id]+=a[i];
	}
	if (flag) tag[id]=1;
}
void modify(int l,int r)
{
	if (pos[l]==pos[r])
	{
		for (int i=l;i<=r;i++) a[i]=sqrt(a[i]);
		change(pos[l],0);
	}
	else
	{
		if (!tag[pos[l]])
		{
			for (int i=l;i<=br[pos[l]];i++) a[i]=sqrt(a[i]);
			change(pos[l],0);
		}
		for (int i=pos[l]+1;i<pos[r];i++) if (!tag[i]) change(i,1);
		if (!tag[pos[r]])
		{
			for (int i=bl[pos[r]];i<=r;i++) a[i]=sqrt(a[i]);
			change(pos[r],0);
		}
	}
}
int query(int l,int r)
{
	int res=0;
	if (pos[l]==pos[r])
	{
		for (int i=l;i<=r;i++) res+=a[i];
		return res;
	}
	else
	{
		for (int i=l;i<=br[pos[l]];i++) res+=a[i];
		for (int i=pos[l]+1;i<pos[r];i++) res+=sum[i];
		for (int i=bl[pos[r]];i<=r;i++) res+=a[i];
		return res;
	}
}
signed main()
{
	read(n);
	for (int i=1;i<=n;i++) read(a[i]);
	init();
	for (int i=1;i<=n;i++)
	{
		int opt,l,r,c;
		read(opt),read(l),read(r),read(c);
		if (opt==0) modify(l,r);
		else writewith(query(l,r),'\n');
		//for (int j=1;j<=n;j++) writewith(a[j],' ');puts("");
	}
	return 0;
}

T6

单点插入,随机访问。

显然的块状链表。感谢 pb_ds 库提供了 rope 容器,因此可以直接用 rope 水过这道题。

#include<bits/stdc++.h>
#include<ext/rope>
#define mem(a,b) memset(a,b,sizeof a)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
//#define int long long
using namespace std;
using namespace __gnu_cxx;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
rope<int> ro;
int n;
signed main()
{
	read(n);
	for (int i=1,temp;i<=n;i++) read(temp),ro.push_back(temp);
	for (int i=1;i<=n;i++)
	{
		int opt,l,r,c;
		read(opt),read(l),read(r),read(c);
		if (opt==0) ro.insert(l-1,r);
		else writewith(ro[r-1],'\n');
	}
}

T7

区间乘,区间加,区间和。

比较麻烦。分别记录乘法和加法的 tag,访问散块的时候用类似线段树的方式下传标记,先乘后加。进行加法的时候对于散块暴力,整块加入加法的 tag。乘法的时候散块暴力,整块同时乘入加法和乘法的 tag 中。最后查询的时候把标记的贡献也加入即可。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=1e5,_BLOCK=1e3,mod=10007;
int pos[_SIZE+5],bl[_BLOCK+5],br[_BLOCK+5];
int tagM[_BLOCK+5],tagA[_BLOCK+5];
int a[_SIZE+5],n,len,cnt;
void init()
{
	len=sqrt(n),cnt=(n-1)/len+1;
	for (int i=1;i<=cnt;i++) 
		bl[i]=(i-1)*len+1,br[i]=min(n,i*len);
	for (int i=1;i<=cnt;i++)
		for (int j=bl[i];j<=br[i];j++)
			pos[j]=i,tagM[i]=1;
}
void pushdown(int id)
{
	for (int i=bl[id];i<=br[id];i++)
		(a[i]=a[i]*tagM[id]+tagA[id])%=mod;
	tagM[id]=1,tagA[id]=0;
}
void add(int l,int r,int c)
{
	if (pos[l]==pos[r])
	{
		pushdown(pos[l]);
		for (int i=l;i<=r;i++) (a[i]+=c)%=mod;
	}
	else
	{
		pushdown(pos[l]);
		for (int i=l;i<=br[pos[l]];i++) (a[i]+=c)%=mod;
		for (int i=pos[l]+1;i<pos[r];i++) (tagA[i]+=c)%=mod;
		pushdown(pos[r]);
		for (int i=bl[pos[r]];i<=r;i++) (a[i]+=c)%=mod;
	}
}
void mul(int l,int r,int c)
{
	if (pos[l]==pos[r])
	{
		pushdown(pos[l]);
		for (int i=l;i<=r;i++) (a[i]*=c)%=mod;
	}
	else
	{
		pushdown(pos[l]);
		for (int i=l;i<=br[pos[l]];i++) (a[i]*=c)%=mod;
		for (int i=pos[l]+1;i<pos[r];i++)
			(tagA[i]*=c)%=mod,(tagM[i]*=c)%=mod;
		pushdown(pos[r]);
		for (int i=bl[pos[r]];i<=r;i++) (a[i]*=c)%=mod;
	}
}
int query(int x)
{
	return (a[x]*tagM[pos[x]]%mod+tagA[pos[x]])%mod;
}
signed main()
{
	read(n);
	for (int i=1;i<=n;i++) read(a[i]);
	init();
	for (int i=1;i<=n;i++)
	{
		int opt,l,r,c;
		read(opt),read(l),read(r),read(c);
		if (opt==0) add(l,r,c);
		else if (opt==1) mul(l,r,c);
		else writewith(query(r),'\n');
	}
	return 0;
}

T8

区间推平,区间查询 \(c\) 的个数。

你都区间推平了,还每次询问都推平,那我不用珂朵莉树来写真的说不过去了。

珂朵莉树支持区间推平操作,时间复杂度取决于你推平的次数。这道题推平的次数很多,因此时间复杂度是正确的,近乎 \(\mathcal O(n\log\log n)\)。

建出珂朵莉树过后直接在珂朵莉树上暴力统计即可。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
struct Node{
	int l,r;
	mutable int v;
	Node(){}
	Node(int l,int r=0,int v=0) : l(l),r(r),v(v) {}
	bool operator< (const Node &a) const {return l<a.l;}
};
set<Node> ctlt;
auto split(int pos)
{
	auto it=ctlt.lower_bound(Node(pos));
	if (it!=ctlt.end() && it->l==pos) return it;
	it--;
	if (it->r<pos) return ctlt.end();
	int l=it->l,r=it->r,v=it->v;
	ctlt.erase(it);
	ctlt.insert(Node(l,pos-1,v));
	return ctlt.insert(Node(pos,r,v)).first;
}
void assign(int l,int r,int v)
{
	auto itr=split(r+1),itl=split(l);
	ctlt.erase(itl,itr);
	ctlt.insert(Node(l,r,v));
}
int query(int l,int r,int c)
{
	auto itr=split(r+1),itl=split(l);
	int res=0;
	for (auto it=itl;it!=itr;it++) if (it->v==c) res+=it->r-it->l+1;
	return res;
}
int n;
signed main()
{
	read(n);
	for (int i=1,temp;i<=n;i++) read(temp),ctlt.insert(Node(i,i,temp));
	for (int i=1;i<=n;i++)
	{
		int l,r,c;read(l),read(r),read(c);
		writewith(query(l,r,c),'\n');
		assign(l,r,c);
	}
	return 0;
}

T9

区间众数。

同洛谷上的 蒲公英 一题。先将数据离散化,然后对值域分块。

查询的时候可以得知,区间的众数只可能是中间整块的众数或者是散块的众数,只统计这两个数就行了。对于整块区间的众数可以预处理。

实现时的一些问题:查询的时候的桶数组不能用 memset 清空,因为 memset 是 \(\mathcal O(n)\) 的,所以会 T 飞。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
//#define int long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
int read()
{
	int k=0,flag=1;char b=getchar();
	while (b<'0' || b>'9') {flag=(b=='-')?-1:1;b=getchar();}
	while (b>='0' && b<='9') {k=(k<<3)+(k<<1)+(b^48);b=getchar();}
	return k*flag;
}
const int _SIZE=1e5;
int n,m;
int len,sum,cnt;
int a[_SIZE+5],b[_SIZE+5];
int val[_SIZE+5],pos[_SIZE+5];
int s[1005][_SIZE+5],f[1005][1005],t[_SIZE+5];
struct BLOCK{
	int l,r;
}block[1005];
void PreWork()
{
	len=sqrt(n);
	sort(b+1,b+n+1);
	sum=unique(b+1,b+n+1)-b-1;
	cnt=(n-1)/len+1;
	for (int i=1;i<=n;i++)
		val[i]=lower_bound(b+1,b+sum+1,a[i])-b;
	for (int i=1;i<=cnt;i++)
	{
		block[i].l=(i-1)*len+1;
		block[i].r=min(n,i*len);
	}
	for (int i=1;i<=cnt;i++)
	{
		for (int j=block[i].l;j<=block[i].r;j++)
			s[i][val[j]]++,pos[j]=i;
		for (int j=1;j<=sum;j++)
			s[i][j]+=s[i-1][j];
	}
	for (int i=1;i<=cnt;i++)
		for (int j=i;j<=cnt;j++)
		{
			int most=f[i][j-1];
			for (int k=block[j].l;k<=block[j].r;k++)
			{
				int appear=s[j][val[k]]-s[i-1][val[k]],mostAppear=s[j][most]-s[i-1][most];
				//printf("%d~%d %d:%d\n",block[i].l,block[j].r,b[val[k]],appear);
				if (appear>mostAppear || (appear==mostAppear && val[k]<most))
					most=val[k];
			}
			f[i][j]=most;
		}
}
int query(int l,int r)
{
	int most=0;
	if (l>r) swap(l,r);
	if (pos[r]-pos[l]<=1)
	{
		for (int i=l;i<=r;i++)
			t[val[i]]++;
		for (int i=l;i<=r;i++)
			if (t[val[i]]>t[most] || (t[val[i]]==t[most] && val[i]<most))
				most=val[i];
		for (int i=l;i<=r;i++) t[val[i]]--;
	}
	else
	{
		for (int i=l;i<=block[pos[l]].r;i++)
			t[val[i]]++;
		for (int i=block[pos[r]].l;i<=r;i++)
			t[val[i]]++;
		most=f[pos[l]+1][pos[r]-1];
		for (int i=l;i<=block[pos[l]].r;i++)
		{
			int mostAppear=s[pos[r]-1][most]-s[pos[l]][most]+t[most];
			int currAppear=s[pos[r]-1][val[i]]-s[pos[l]][val[i]]+t[val[i]];
			if (currAppear>mostAppear || (currAppear==mostAppear && val[i]<most))
				most=val[i];
		}
		for (int i=block[pos[r]].l;i<=r;i++)
		{
			int mostAppear=s[pos[r]-1][most]-s[pos[l]][most]+t[most];
			int currAppear=s[pos[r]-1][val[i]]-s[pos[l]][val[i]]+t[val[i]];
			if (currAppear>mostAppear || (currAppear==mostAppear && val[i]<most))
				most=val[i];
		}
		for (int i=l;i<=block[pos[l]].r;i++)
			t[val[i]]--;
		for (int i=block[pos[r]].l;i<=r;i++)
			t[val[i]]--;
	}
	return b[most];
}
signed main()
{
	//freopen("P4168.in","r",stdin);
	n=read(),m=n;
	for (int i=1;i<=n;i++) a[i]=b[i]=read();
	PreWork();
	//for (int i=1;i<=cnt;i++) {printf("1~%d ",block[i].r);for (int j=1;j<=sum;j++){printf("%d:%d | ",b[j],s[i][j]);}puts("");}
	//for (int i=1;i<=cnt;i++) printf("%d~%d %d\n",block[i].l,block[i].r,b[f[i][i]]);
	int x=0;
	while (m--)
	{
		int l=read(),r=read();
		x=query(l,r);
		printf("%d\n",x);
	}
	return 0;
}

标签:10,入门,分块,LOJ,void,write,int,flag,getchar
From: https://www.cnblogs.com/hanx16msgr/p/16788758.html

相关文章

  • 多项式简陋入门
    多项式全家桶然而并没有多点求值,快速插值,转下降/上升幂,复合,复合逆疯狂多项式,v我50namespaceefX_poly{ constintmaxlen=(1<<23)+1,maxSqrt=1e5+1; inlineintad......
  • DOS出初入门学习
    打开CMD方式开始+系统+命令提示符Windows+R打开运行,输入cmd打开控制台(推荐使用)在任意位置按住Shift+鼠标右键点击,在此处打开Powershall窗口资源管理器的地址栏......
  • Salesforce入门课程,清理非活跃用户这8点必须要注意!
     非活跃用户确实给Salesforce管理员带来了诸多问题。创建用户后,可以将其设为非活跃状态,但永远不能将其删除。随着时间的推移,大多数组织的非活跃用户会远多于活跃用户。......
  • C++入门(上)
    ......
  • 7天入门小程序开发 | 07-小程序用户登录及多账号管理
            回顾下之前的内容,我们能够设计小程序页面,能够实现用户与数据库之间的交互,也已经使用了云开发中的云存储、数据库、云函数,能够从前到后实现简单小程序的开发......
  • 3-Air724UG模块(4G全网通GPRS开发)-下载DTU固件和入门使用
    <p><iframename="ifd"src="https://mnifdv.cn/resource/cnblogs/LearnAir724UG"frameborder="0"scrolling="auto"width="100%"height="1500"></iframe></p> 说明......
  • WEB简介与HTTP入门
    一、Web简介1、什么是Web学习Web安全当然要简单的了解什么是Web,Web与生活息息相关,上个网站浏览新闻,看个视频等其中涉及到几个基本的点。从通信,会接触到URL,到协议,......
  • 软件测试入门学习
    caohongxing的博客​软件测试2小时入门​​​https://study.163.com/course/courseMain.htm?courseId=1004794006&trace_c_p_k2=debbdb37dde34011af67c8e4f996a17a​​......
  • Markdown入门
    Markdown轻松入门刚注册的博客园来记录小白成长笔记,第一天先来打个卡吧!Markdown是一种语法,这种语法需要Typora编辑器结合,有好多编辑器都支持这种语法,例如印象笔记、博客......
  • 7.函数入门
    函数函数的作⽤函数的使⽤步骤函数的参数作⽤函数的返回值作⽤函数的说明⽂档函数嵌套函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能......