• 【题解】「LibreOJ β Round #7」某少女附中的体育课


    题目链接

    题目大意:定义(m)进制下的运算符(oplus),它满足交换律,结合律,循环律,给出它在任意位上的其运算规则(A_{i,j}),表示(ioplus j=A_{i,j}),从([0,m^n))中以一定概率选出(k+1)个数(s_0,s_2dots,s_k),求([0,m^n))中每个数恰好等于(s_0oplus s_1oplusdotsoplus s_k)的概率。对(232792561=2^4 imes3^2 imes5 imes7 imes11 imes13 imes17 imes19)取模。

    神题啊,我看了两天题解才看懂

    首先,根据标签,可以想到题目是要让你构造一个变换及其逆变换,使得原概率序列(p_0,p_1,dots,p_{m^n-1})经该变换后,对每一位求(k)次方后再进行逆变换可以得到答案序列。

    于是可以设变换矩阵为(T),则有(p imes T)(q imes T)对应位置相乘后与((p*q) imes T)相等,即(T_{k,i} imes T_{k,j}=T_{k,ioplus j})

    因为该运算满足循环律,设满足(i^j=i(j>1))的最小的(j)(len_i),类比( ext{DFT})的转移矩阵,可以猜测(T_{i,j})(0)(len_j-1)次单位根的([0,len_j-1))次幂。

    然后就可以根据前面的规律(O(m^m))加剪枝来爆搜出此矩阵,求逆得逆变换矩阵,再对(p)进行变换,就可以得到目标数组。

    具体(神仙)证明

    code:

    #include<stdio.h>
    #include<algorithm>
    #define inf 232792561
    int a[102][202],ts[102],st[102],l=0;
    int p[102][102],q[102][102],w[102][102],t[102][102];
    int n,m;long long k;
    int A[1048576],B[1048576];
    inline int ksm(long long a,long long b){int ans=1;while(b)(b&1)&&(ans=a*ans%inf),a=a*a%inf,b>>=1;return ans;}
    inline void gs(int n,int m){
    	for(int i=0;i<n;i++){
    		int pos=i;
    		while(pos<n&&!a[pos][i])pos++;
    		if(pos>=n)exit(-1);
    		if(pos!=i)
    			for(int j=i;j<m;j++)
    				a[i][j]^=a[pos][j]^=a[i][j]^=a[pos][j];
    		int val=ksm(a[i][i],inf-2);
    		for(int j=i;j<m;j++)a[i][j]=1ull*val*a[i][j]%inf;
    		for(int j=0;j<n;j++)
    			if(i!=j&&a[j][i])
    				for(int k=m-1;k>=i;k--)
    					a[j][k]=(1ull*(inf-a[j][i])*a[i][k]+a[j][k])%inf;
    	}
    }
    void dfs(int pos){
    	if(l==m)return;
    	if(pos==m){
    		for(int i=0;i<m;i++)p[l][i]=st[i];
    		l++;return;
    	}bool ff=st[pos]=0;
    	for(int i=0;i<pos;i++){
    		for(int j=i;j<pos;j++){
    			if(t[i][j]==pos){
    				if(!ff)st[pos]=1ll*st[i]*st[j]%inf,ff=1;
    				else if(st[pos]!=1ll*st[i]*st[j]%inf)return;
    			}
    		}
    	}if(ff){
    		for(int j=0;j<=pos;j++){
    			if(t[j][pos]<=pos&&1ll*st[j]*st[pos]%inf!=st[t[j][pos]])return;
    		}dfs(pos+1);
    	}
    	else{
    		for(int i=0;i<=ts[pos];i++){
    			st[pos]=w[ts[pos]][i];
    			bool f=1;
    			for(int j=0;j<=pos;j++){
    				if(t[j][pos]<=pos&&1ll*st[j]*st[pos]%inf!=st[t[j][pos]])f=0;
    			}if(f)dfs(pos+1);
    		}
    	}
    }
    void ntt(int a[],int b[],int n,bool typ){
    	int dt=n/m,p1,p2;
    	if(dt!=1)for(int i=0;i<n;i+=dt)ntt(a+i,b,dt,typ);
    	for(int i=0;i<n;i++)b[i]=0;
    	for(int i=0;i<m;i++)
    		for(int j=0;j<m;j++)
    			for(int k=0;k<dt;k++){
    				int p1=i*dt+k,p2=j*dt+k;
    				b[p1]=(1ll*a[p2]*(typ?q[i][j]:p[i][j])+b[p1])%inf;
    			}
    	for(int i=0;i<n;i++)a[i]=b[i];
    }
    int main(){
    	scanf("%d%d%lld",&n,&m,&k);
    	n=ksm(m,n);
    	for(int i=0;i<m;i++)
    		for(int j=0;j<m;j++)
    			scanf("%d",&t[i][j]);
    	for(int i=1;i<=m;i++){
    		w[i][0]=1;
    		if(i!=1)w[i][1]=ksm(71,(inf-1)/i);
    		for(int j=2;j<i;j++)
    			w[i][j]=1ll*w[i][j-1]*w[i][1]%inf;
    	}for(int i=0;i<m;i++){
    		int P=i;ts[i]=0;
    		while(1){
    			ts[i]++;
    			P=t[P][i];
    			if(P==i)break;
    		}
    	}dfs(0);
    	for(int i=0;i<m;i++)
    		for(int j=0;j<m;j++)
    			a[i][j]=p[i][j];
    	for(int i=0;i<m;i++)
    		a[i][i+m]=1;
    	gs(m,m+m);
    	for(int i=0;i<m;i++)
    		for(int j=0;j<m;j++)
    			q[i][j]=a[i][j+m];
    	for(int i=0;i<n;i++)scanf("%d",&A[i]);
    	ntt(A,B,n,0);
    	for(int i=0;i<n;i++)A[i]=ksm(A[i],k+1);
    	ntt(A,B,n,1);
    	for(int i=0;i<n;i++)printf("%d
    ",A[i]);
    }
    
  • 相关阅读:
    将Ubuntu18.04安装到U盘,实现即插即用
    小技巧之 前端自适应
    web笔记之 环境搭建
    C++学习笔记之 单例模式
    推荐一款Markdown编辑器:typora
    C++学习笔记之 类和对象
    C++学习笔记之 函数
    C++学习笔记之 内联函数
    C++学习笔记之 引用
    C++学习笔记之 const
  • 原文地址:https://www.cnblogs.com/ztc03/p/11964881.html
Copyright © 2020-2023  润新知