【题解】「LibreOJ β Round #7」某少女附中的体育课
题目大意:定义\(m\)进制下的运算符\(\oplus\),它满足交换律,结合律,循环律,给出它在任意位上的其运算规则\(A_{i,j}\),表示\(i\oplus j=A_{i,j}\),从\([0,m^n)\)中以一定概率选出\(k+1\)个数\(s_0,s_2\dots,s_k\),求\([0,m^n)\)中每个数恰好等于\(s_0\oplus s_1\oplus\dots\oplus s_k\)的概率。对\(232792561=2^4\times3^2\times5\times7\times11\times13\times17\times19\)取模。
神题啊,我看了两天题解才看懂
首先,根据标签,可以想到题目是要让你构造一个变换及其逆变换,使得原概率序列\(p_0,p_1,\dots,p_{m^n-1}\)经该变换后,对每一位求\(k\)次方后再进行逆变换可以得到答案序列。
于是可以设变换矩阵为\(T\),则有\(p\times T\)与\(q\times T\)对应位置相乘后与\((p*q)\times T\)相等,即\(T_{k,i}\times T_{k,j}=T_{k,i\oplus j}\)。
因为该运算满足循环律,设满足\(i^j=i(j>1)\)的最小的\(j\)为\(len_i\),类比\(\text{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\n",A[i]);
}
Please not contact lydsy2012@163.com!