luogu P7125 [Ynoi2008] rsmemq


题面传送门
为啥这个题空间只开128M啊,反正\(O(n\sqrt n)\)空间都能卡掉开个256M造福人类不好吗?
首先考虑对于每种颜色计算答案,容易想到对于每个点向左向右跑,总共只会有\(O(n)\)个区间会有答案的贡献,这个贡献形如\(x+y=k\),然后\(l\leq x\leq r,l\leq y\leq r\)的二维数点。
但是有两个问题,首先这个二维数点不是很可做,其次这\(O(n)\)个区间的复杂度会被卡到\(O(n^2)\)
先来看第二个问题。考虑一个simple的做法:对于两个端点之间这个数的出现次数显然是不变的,所以可以直接二分,然后分块查询区间众数,时间复杂度\(O(n\sqrt n\log n)\)一脸不可过的亚子。
注意到所有颜色的出现次数之和是\(O(n)\)的所以可以根号分治。对于出现次数大于\(B\)的直接暴力跑就好了,对于出现次数小于\(B\)的因为这个区间众数的出现次数也不大于\(B\),所以可以每个点two-points跑出以这个点为右端点且众数出现次数不大于\(i\)的最大左端点,然后二分的时候直接查就好了。时间复杂度\(O(n\sqrt n)\)
但是空间似乎是\(O(n\sqrt n)\)的?
发现其实可以在每次处理\(i\)的时候二分,就可以变成\(O(n)\)了。
然后二维数点部分其实也很simple,考虑发现不管是查询还是修改都是对称的,所以对于每个询问可以将修改分成对称轴在左边和对称轴在右边。然后两边分别只有一边的限制有效,然后就直接树状数组就好了。
本来写了个线段树然后被卡空间了
然后就得到了一个时间复杂度\(O(n\sqrt n+q\log n)\),空间\(O(n+m)\)的做法,贴着空间限制跑过。
code:

#include
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (500000+5)
#define M (100+5)
#define K (1000+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
struct IO{
    static const int S=1<<19;
    char buf[S],*p1,*p2;int st[105],Top;
    ~IO(){clear();}
    inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
    inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
    inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}
    inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='\r');return *this;}
    templateinline IO&operator >> (T&x){
        x=0;bool f=0;char ch=gc();
        while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
        f?x=-x:0;return *this;
    }
    inline IO&operator << (const char c){pc(c);return *this;}
    templateinline IO&operator << (T x){
        if(x<0) pc('-'),x=-x;
        do{st[++st[0]]=x%10,x/=10;}while(x);
        while(st[0]) pc('0'+st[st[0]--]);return *this;
    }
}fin,fout;
using namespace std;
int R[N],X[N],Y[N],n,m,k,Bh,B[N],Fl[N],Ps[N],l,r,mid,Tp,Id,A[N],Fs;ll Ans[N];vector S[N],C[N],Q[N];
struct Ques{int x,y;};vector P[N];
namespace Tree{
	int F[N];ll G[N];I void Ins(int x,int w1,int w2){while(x<=n) F[x]+=w1,G[x]+=w2,x+=x&-x;}
	I int Q1(int x){int Ans=0;while(x) Ans+=F[x],x-=x&-x;return Ans;}I ll Q2(int x){ll Ans=0;while(x) Ans+=G[x],x-=x&-x;return Ans;}
	I void Ins(int x,int y){Ins(x,1,-x+1);Ins(y+1,-1,y);}I ll Find(int x,int y){return 1ll*Q1(y)*y+Q2(y)-(1ll*Q1(x-1)*(x-1)+Q2(x-1));}
}
int main(){
	freopen("1.in","r",stdin);
	int i,j,h;fin>>n>>m;k=sqrt(n)/5;for(i=1;i<=n;i++) fin>>A[i],2*A[i]-i>=1&&2*A[i]-i<=n&&(S[A[i]].PB(i),0);
	for(i=1;i<=n;i++){
		if(S[i].size()=min(i,n-i+1)) break;Fs=0;
			for(h=B[j];h=k) continue;for(int j:S[i]) abs(j-i)i) {Ps[Fl[A[R[j]]]]--,Fl[A[R[j]]]--,Ps[Fl[A[R[j]]]]++;while(!Ps[Tp]) Tp--;R[j]++;}
			Tp^i&&(R[j]=0);
		}
		for(j=1;j<=n;j++){
			if(C[j].size()<=i||S[j].size()>=k) continue;l=j+C[j][i-1]-1;r=j+C[j][i];while(l+1>1,(R[mid]<=2*j-mid?l:r)=mid;2*j-l<=j-C[j][i-1]&&(P[j].PB((Ques){2*j-l,j-C[j][i-1]}),0);
		}
	}
	for(i=1;i<=m;i++) fin>>X[i]>>Y[i],Q[X[i]+Y[i]>>1].PB(i);
	for(i=1;i<=n;i++){for(Ques j:P[i]) j.x<=j.y&&(Tree::Ins(j.x,j.y),0);for(int j:Q[i]) Ans[j]+=Tree::Find(X[j],n);}
	Me(Tree::F,0);Me(Tree::G,0);for(i=1;i<=n;i++) Q[i].clear();for(i=1;i<=m;i++) Q[(X[i]+Y[i]>>1)+1].PB(i);
	for(i=n;i;i--) {for(Ques j:P[i]) j.x<=j.y&&(Tree::Ins(2*i-j.y,2*i-j.x),0);for(int j:Q[i]) Ans[j]+=Tree::Find(1,Y[j]);}for(i=1;i<=m;i++) fout<