WC2020 猜數遊戲
阿新 • • 發佈:2020-08-06
猜數遊戲
黑板上寫有 \(n\) 個互不相等且都小於 \(p\) 的正整數 \(a_1, a_2, \cdots, a_n\)。小 J 想用這些數字和小 M 玩一個猜數遊戲。
遊戲規則十分簡單:遊戲開始時,小 J 會從這些數字中隨機選擇若干個讓小 M 來猜,而小 M 則可以通過若干次詢問來確定小 J 選擇了哪些數字。
每一次詢問的模式如下:小 M 可以任意指定一個數字 \(a_k\),若它是小 J 所選擇的數字之一,則小 J 會告訴小 M 他所選擇的數字中所有能表示成 \((a_k)^m \bmod p\) 的數,其中 \(m\) 是任意正整數,\(\bmod\) 表示求二者做帶餘除法後的餘數。反之,若 \(a_k\)
遊戲會在小 M 確定小 J 所選中的所有數字後立刻結束。
例如,若 \(n=4\),\(p=7\),數字 \(\{a_n\}\) 按下標順序依次為 \(\{1, 3, 4, 6\}\),小 J 選定的數字為 \(\{1, 4, 6\}\),一種可能的遊戲進行的過程(並非是最優過程)如下:
小 M 的詢問 | 小 J 的反饋 |
---|---|
\(a_2 = 3\) | \(a_2\) 沒有被選中 |
\(a_4 = 6\) | \(6(= 6^1 \bmod 7)\),\(1(=6^2 \bmod 7)\) |
\(a_3 = 4\) |
\(4(= 4^1 \bmod 7)\),\(1(=4^3 \bmod 7)\) |
\(3\) 次詢問後小 J 所選出的所有數都已被小 M 確定,遊戲結束。
小 M 還有作業沒有寫完,因此他需要對遊戲進行的時間進行評估。他想知道為了使遊戲結束,他所需要做出詢問的最小次數的期望 \(S\) 是多少。
為了避免精度誤差,你需要輸出答案乘 \((2^n - 1)\) 後模 \(998244353\) 的餘數。在本題中,你可以認為小 J 每次在選數時會在集合 \(\{a_1, a_2, \cdots, a_n\}\) 的全部非空子集中等概率地選擇一個,在這個前提下可以證明 \((2^n - 1) \times S\)
題解
暫時是LOJ和UOJ第一名還行。
int pow(int a,int b,int mod){
int ans=1;
for(;b;b>>=1,a=(int64)a*a%mod)
if(b&1) ans=(int64)ans*a%mod;
return ans;
}
int order(int a,int p,int phi,CO vector<int>&d){
int o=phi;
for(int x:d)while(o%x==0 and pow(a,o/x,p)==1) o/=x;
return o;
}
unordered_map<int,int> cnt,deg;
vector<pair<int,int> > vec;
int main(){
int n=read<int>(),p=read<int>();
int q=p,k=1;
for(int i=2;i*i<=p;++i)if(p%i==0){
q=i,k=0;
for(int x=p;x%i==0;x/=i) ++k;
break;
}
int phi=q-1;
for(int i=2;i<=k;++i) phi*=q;
vector<int> d;
if(k>1) d.push_back(q);
int x=q-1;
for(int i=2;i*i<=x;++i)if(x%i==0){
d.push_back(i);
while(x%i==0) x/=i;
}
if(x>1) d.push_back(x);
for(int i=1;i<=n;++i){
int a=read<int>();
if(a%q!=0){
int o=order(a,p,phi,d);
++cnt[phi/o];
}
else{
deg[a]=0;
int e=0;
for(int x=a;x%q==0;x/=q) ++e;
vec.push_back({e,a});
}
}
int ans=0;
for(int i=1;i*i<=phi;++i)if(phi%i==0){
int sum=-cnt[i];
for(int j=1;j*j<=i;++j)if(i%j==0){
sum+=cnt[j];
if(i/j==j) continue;
sum+=cnt[i/j];
}
ans=add(ans,mul(fpow(i2,sum),1+mod-fpow(i2,cnt[i])));
if(phi/i==i) continue;
sum=-cnt[phi/i];
for(int j=1;j*j<=phi/i;++j)if(phi/i%j==0){
sum+=cnt[j];
if(phi/i/j==j) continue;
sum+=cnt[phi/i/j];
}
ans=add(ans,mul(fpow(i2,sum),1+mod-fpow(i2,cnt[phi/i])));
}
sort(vec.begin(),vec.end());
for(CO pair<int,int>&a:vec){
ans=add(ans,mul(fpow(i2,deg[a.second]),i2));
for(int x=a.second;x!=0;x=(int64)x*a.second%p)
if(deg.count(x)) ++deg[x];
}
ans=mul(ans,fpow(2,n));
write(ans,'\n');
return 0;
}