看节目

### Description

​  大概就是给定你一种用两个参数\(X\)\(Y\)随机一个\(1\sim n\)的排列\(p\)的方式,然后给定一个数组\(a\),现在有两种操作:

​  操作\((1,k)\)表示进行\(k\)次重新排列,一次重新排列指的是令新的\(a\)中第\(p_i\)个位置的值等于原来\(a\)中的第\(i\)个位置的值

​  操作\((2,l,r)\)表示查询\(\sum\limits_{i=l}^r a_i\),答案对\(998244353\)取模

​  数据范围:\(1\leq n,m\leq 10^5,233\leq X,Y<998244353,0\leq a_i<998244353,1\leq k\leq 10^9\)

​  

Solution

​  emmm好像第一次碰到这么神秘的分块题qwq记录一下(流下了做题少的泪水qwq)

​  

​​  考虑一下随机出来的置换\(p\)的性质

​  我们考虑将这个置换拆成若干个环(\(i\rightarrow p_i\)),记\(h_n\)表示长度为\(n\)的排列的期望环数,考虑从\(h_{n-1}\)推到\(h_n\),那么第\(n\)个元素有\(\frac{1}{n}\)的概率自环,其余情况并入前面的环中,不会增加环数,于是有\(h_n=h_{n-1}+\frac{1}{n}\),调和级数,渐进\(O(logn)\)

​  我们将询问差分一下,这样就变成了若干个前缀和的询问,形如\((x,k)\),表示的是查询重新排列了\(k\)次之后的前缀\(x\)的和,而拆成环之后重新排列其实就相当于在环上面跳(注意是反着跳)

​  因为环数有保证,所以我们可以考虑对于每个环单独计算贡献:对于一个环\(i\),记\(len_i\)该环的长度,\(val_i\)表示该环中的元素破成链之后的结果(下标从\(0\)开始),\(loc_i(k)\)表示重排\(k\)次之后\(i\)的位置,\(f_{i,k}(x)\)表示环\(i\)重排了\(k\)次之后的前缀和,那么有:

\[f_{i,k}(x)=\sum\limits_{i=0}^{len-1}val_i[loc_i(k)\%len_i\leq x] \]

​​  这个时候就有一个很神秘的做法了:考虑对\(x\)进行分块,我们把所有的询问\((x,k)\)离线(其实在线好像也可以,只是分块维护的数组多一维。。?),按照询问的\(x\)排序,然后一块一块地处理,对于一个\(St\sim Ed\)的块,处理所有满足\(St\leq x\leq Ed\)的询问

​  这样的话我们就需要维护一个数组\(sum[i][j]\),假设当前已经处理完的最后一个块的结尾为\(ed\),那么\(sum[i][j]\)表示第\(i\)个环重排了\(ed\)次之后前\(j\)位的和(也就是\(sum[i][j]=\sum\limits_{i}f_{i,ed}(j)\)),那么对于一个查询\((x,k)\),我们枚举每一个环,将\(sum[i][k\%len_i]\)加入贡献,然后再暴力枚举一下当前块的开始位置\(st\sim x\)的每个位置,计算重排\(k\)次之后这些位置的值的和再加入贡献中即可

​  那么最后的问题就是怎么计算\(sum[i][j]\),我们每处理完一个块中的询问之后,都要将\(ed\)移动到这个块的结尾并重新计算\(sum\),注意到\(f_{i,k}(x)\)其实是一个卷积形式,所以直接NTT就好了(然而实际上因为\([(i+k)\% len_i\leq x]\in \{0,1\}\),所以直接FFT最后再取模好像也没有什么问题,会快)

​​  

​  时间复杂度的话,记块大小为\(T\),那么是\(O(\frac{n}{T}nlogn+Tn)\)的,然后当\(T\)\(\sqrt{nlogn}\)的时候复杂度为\(O(n\sqrt{nlogn})\)

​  

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
#define pb push_back
#define ll long long
using namespace std;
const int N=1e5+10,MOD=998244353;
struct Q{
	int x,id,op;
	ll k;
	Q(int _x=0,int _id=0,int _op=0,ll _k=0){x=_x; id=_id; op=_op; k=_k;}
	friend bool operator < (Q x,Q y){return x.x<y.x;}
}recq[N*2];
vector<int> cir[N];
int len[N],bl[N],loc[N];
int a[N],ans[N],p[N];
int n,m,X,Y;
int cntq,cntcir;
ll K;
int mul(int x,int y){return 1LL*x*y%MOD;}
int plu(int x,int y){y+=y<0?MOD:0; return (1LL*x+y)-(1LL*x+y>=MOD?MOD:0);}
int ksm(int x,int y){
	int ret=1,base=x;
	for (;y;y>>=1,base=mul(base,base))
		if (y&1) ret=mul(ret,base);
	return ret;
}
namespace NTT{/*{{{*/
	const int TOP=18,N=(1<<TOP)+10,G=3;
	int A[N],B[N],W[TOP+1][N][2];
	int rev[N];
	int n,len,invlen;
	void init(){
		int invg=ksm(G,MOD-2);
		int x,invx;
		for (int step=2,lg=1;step<N;step<<=1,++lg){
			x=ksm(G,(MOD-1)/step);
			invx=ksm(x,MOD-2);
			W[lg][0][0]=W[lg][0][1]=1;
			for (int i=1;i<(step>>1);++i){
				W[lg][i][0]=mul(W[lg][i-1][0],x);
				W[lg][i][1]=mul(W[lg][i-1][1],invx);
			}
		}
	}
	void getlen(int n){
		for (int i=0;i<len;++i) A[i]=B[i]=0;
		int bit=0;
		for (len=1;len<=n;len<<=1,++bit);
		rev[0]=0;
		for (int i=1;i<=len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
		invlen=ksm(len,MOD-2);
	}
	void ntt(int *a,int op){
		int u,v;
		for (int i=0;i<len;++i) if (rev[i]>i) swap(a[i],a[rev[i]]);
		for (int step=2,lg=1;step<=len;step<<=1,++lg)
			for (int st=0;st<len;st+=step)
				for (int i=0;i<(step>>1);++i){
					v=mul(a[st+i+(step>>1)],W[lg][i][op==-1]);
					u=a[st+i];
					a[st+i]=plu(u,v);
					a[st+i+(step>>1)]=plu(u,MOD-v);
				}
		if (op==1) return;
		for (int i=0;i<len;++i) a[i]=mul(a[i],invlen);
	}
	void calc(){
		ntt(A,1);
		ntt(B,1);
		for (int i=0;i<len;++i) A[i]=mul(A[i],B[i]);
		ntt(A,-1);
	}
}/*}}}*/
namespace Block{/*{{{*/
	const int LG=30;
	int sum[LG][N];
	int sq,num;
	int st,ed;
	int Id(int x){return (x-1)/sq+1;}
	int St(int x){return (x-1)*sq+1;}
	int Ed(int x){return min(n,x*sq);}
	int query(int x,ll k){
		int ret=0,id=Id(x),pos;
		for (int i=1;i<=cntcir;++i)
			ret=plu(ret,sum[i][k%len[i]]);
		int which;
		for (int i=st;i<=x;++i){
			which=bl[i];
			pos=(loc[i]-k%len[which]+len[which])%len[which];
			ret=plu(ret,a[cir[which][pos]]);
		}
		return ret;
	}
	void solve(){
		int now=1,tmp,x;
		sq=sqrt(n*log(n)/log(2.0));
		num=Id(n);
		for (int id=1;id<=num;++id){
			st=St(id); ed=Ed(id);
			while (now<=cntq&&recq[now].x<=ed){
				if (now==cntq)
					int debug=1;
				tmp=query(recq[now].x,recq[now].k);
				ans[recq[now].id]=plu(ans[recq[now].id],tmp*recq[now].op);
				++now;
			}
			if (now>cntq) break;

			for (int i=1;i<=cntcir;++i){
				NTT::getlen(len[i]*2);
				for (int j=0;j<len[i];++j) NTT::A[j+1]=a[cir[i][(len[i]-1)-j]];
				for (int j=0;j<len[i];++j) NTT::B[j]=(cir[i][j]<=ed);
				NTT::calc();
				for (int j=0;j<len[i];++j) 
					sum[i][j]=plu(NTT::A[j],NTT::A[j+len[i]]);
			}
		}
		int debug=1;
	}
}/*}}}*/
int myrand(int l,int r){
	X=1LL*X*Y%MOD;
	return X%(r-l+1)+l;
}
void make_per(int *p,int n){
	for (int i=1;i<=n;++i){
		p[i]=i;
		swap(p[myrand(1,i)],p[i]);
	}
}
void prework(){
	int tmp;
	cntcir=0;
	for (int i=1;i<=n;++i){
		if (bl[i]) continue;
		++cntcir; tmp=-1;
		while (!bl[i]){
			bl[i]=cntcir; loc[i]=++tmp;
			cir[cntcir].pb(i);
			i=p[i];
		}
		len[cntcir]=cir[cntcir].size();
	}
}
int read(){
	int ret=0; char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while ('0'<=ch&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
	return ret;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	int op,x,l,r,tmpcnt=0;
	cntq=0;
	NTT::init();
	n=read(); m=read(); X=read(); Y=read();
	make_per(p,n);
	for (int i=1;i<=n;++i) a[i]=read();
	cntq=0; K=0;
	prework();
	for (int i=1;i<=m;++i){
		op=read();
		if (op==1)
			K+=read();
		else{
			l=read(); r=read();
			++tmpcnt;
			recq[++cntq]=Q(l-1,tmpcnt,-1,K);
			recq[++cntq]=Q(r,tmpcnt,1,K);
		}
	}
	sort(recq+1,recq+1+cntq);
	Block::solve();
	for (int i=1;i<=(cntq+1>>1);++i) printf("%d\n",ans[i]);
}
posted @ 2019-01-13 10:12  yoyoball  阅读(321)  评论(0编辑  收藏  举报