zoukankan      html  css  js  c++  java
  • 看节目

    ### Description

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

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

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

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

    ​  

    Solution

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

    ​  

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

    ​  我们考虑将这个置换拆成若干个环((i ightarrow 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)=sumlimits_{i=0}^{len-1}val_i[loc_i(k)\%len_ileq x] ]

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

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

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

    ​​  

    ​  时间复杂度的话,记块大小为(T),那么是(O(frac{n}{T}nlogn+Tn))的,然后当(T)(sqrt{nlogn})的时候复杂度为(O(nsqrt{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
    ",ans[i]);
    }
    
  • 相关阅读:
    CSS样式更改_2D转换
    使用本地json-server服务,创建简单的本地api数据
    为何不推荐使用 Sass 作为 css 预处理器
    移动端适配
    html 元素垂直水平居中
    场内场外基金和开户避坑
    QJson
    Merry Christmas Mr. Lawrence
    github,源码,高仿 直播
    P1314 聪明的质监员
  • 原文地址:https://www.cnblogs.com/yoyoball/p/10261974.html
Copyright © 2011-2022 走看看