【洛谷P4706】取石子
阿新 • • 發佈:2018-08-07
include int rip code cpp esp 一次 res lse ,隨機選擇正整數個棋子 \(y\) ,隨機轉移到一個能轉移到的點 \(z\) 。所有棋子可以看作是一樣的,換句話說:兩種操作不同,當且僅當三元組 \((x, y, z)\) 不同。之後雙方都按照最優策略來操作。
? Yopilla 想要預測,他能夠獲勝的概率是多少,答案對 \(998244353\) 取模。
?
? 記輸入數列為\(a\),我們統計出所有處於奇位置的數\(x\)的\(a_x\)的異或和\(sum\)。
? 我們要統計Yopilla一開始的隨機操作一共有多少種可能、以及總共有多少種可能,使得操作後局面的先手必敗。前者很好計算,就是\(\sum_x a_x*b_x\),其中\(b_x\)表示\(x\)這個數的不同質因子個數。
? 後者如何計算呢?對操作分類:(1)移動奇位置的數至偶位置、(2)移動偶位置的數至奇位置。
? 我們枚舉所有奇位置的數。假設對該位置\(i\)操作後,總異或和\(sum\)等於0,即操作後先手必敗,則\(a_i\)應該由\(a_i\) 變成\(target=sum\; \text{xor}\; a_i\),
如果原值比目標值大,那麽顯然(1)容易滿足,選出\(a_i-target\)個數,並將它們通過任意一個質因子移動到偶位置,一共有\(b_i\)種合法情況。
如果原值與目標值相等,則什麽也做不了,一改就不滿足要求,不作為合法情況考慮。
若原值小於目標值,則考慮(2),枚舉所有能轉移到\(i\)的偶位置\(j=i*p\)(其中\(p\)是枚舉的質數),如果\(a_j \ge target-a_i\),那麽合法情況就多了一種,因為\(j\)可以選\(target-a_i\)個數通過唯一一種方式——除去\(p\)——來到達\(i\)。
? 那麽概率也就很好計算了。
Description
? 現在 Yopilla 和 yww 要開始玩遊戲!
? 他們在一條直線上標記了 \(n\) 個點,從左往右依次標號為 \(1, 2, ..., n\) 。然後在每個點上放置一些棋子,其中第 \(i\) 個點放置了 \(a_i\) 個棋子。接下來,從 Yopilla 開始操作,雙方輪流操作,誰不能操作誰輸。每次的操作是:當前操作方選定一個有棋子的點 \(x\) ,然後選擇至少一個點 \(x\) 上的棋子,然後把這些棋子全都移動到點 \(x / prime\) 上,其中 \(prime\) 是一個質數,且 \(prime \mid x\)
? Yopilla 最初一次操作的策略是隨機的:隨機找到一個有棋子的點 \(x\)
? Yopilla 想要預測,他能夠獲勝的概率是多少,答案對 \(998244353\) 取模。
?
Solution
? 我們發現,對於每一個數,如果以其冪指數之和為下標來將它們重新排列成一個數組,這個問題就變成了階梯\(Nim\)問題。一次操作,相當於將一個數移動到其左邊。不能移動者輸。
? 事實上我們不需要實現這個重排操作。我們只需要知道每個數重排後是否在奇位置即可。
? 記輸入數列為\(a\),我們統計出所有處於奇位置的數\(x\)的\(a_x\)的異或和\(sum\)。
? 我們要統計Yopilla一開始的隨機操作一共有多少種可能、以及總共有多少種可能,使得操作後局面的先手必敗。前者很好計算,就是\(\sum_x a_x*b_x\),其中\(b_x\)表示\(x\)這個數的不同質因子個數。
? 後者如何計算呢?對操作分類:(1)移動奇位置的數至偶位置、(2)移動偶位置的數至奇位置。
? 我們枚舉所有奇位置的數。假設對該位置\(i\)操作後,總異或和\(sum\)等於0,即操作後先手必敗,則\(a_i\)應該由\(a_i\)
如果原值比目標值大,那麽顯然(1)容易滿足,選出\(a_i-target\)個數,並將它們通過任意一個質因子移動到偶位置,一共有\(b_i\)種合法情況。
如果原值與目標值相等,則什麽也做不了,一改就不滿足要求,不作為合法情況考慮。
若原值小於目標值,則考慮(2),枚舉所有能轉移到\(i\)的偶位置\(j=i*p\)(其中\(p\)是枚舉的質數),如果\(a_j \ge target-a_i\),那麽合法情況就多了一種,因為\(j\)可以選\(target-a_i\)個數通過唯一一種方式——除去\(p\)——來到達\(i\)。
? 那麽概率也就很好計算了。
Code
#include <cstdio>
using namespace std;
const int N=1000005,MOD=998244353;
int n,a[N];
bool vis[N];
int p[N],pcnt,b[N],c[N];
void sieve(){
int up=1e6;
for(int i=2;i<=up;i++){
if(!vis[i]){
p[++pcnt]=i;
b[i]=c[i]=1;
}
for(int j=1;j<=pcnt&&i*p[j]<=up;j++){
int x=i*p[j];
vis[x]=true;
c[x]=c[i]^1;
if(i%p[j]==0){
b[x]=b[i];
break;
}
b[x]=b[i]+1;
}
}
}
void readData(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
}
inline int fmi(int x,int y){
int res=1;
for(;y;x=1LL*x*x%MOD,y>>=1)
if(y&1) res=1LL*res*x%MOD;
return res;
}
void solve(){
int x=0;
for(int i=1;i<=n;i++)
if(c[i]) x^=a[i];
int legal=0;
for(int i=1;i<=n;i++)
if(c[i]){
int best=x^a[i];
if(best<a[i]) legal+=b[i];
else{
int delta=best-a[i];
if(!delta) continue;
for(int j=1;j<=pcnt&&i*p[j]<=n;j++)
if(a[i*p[j]]>=delta) legal++;
}
}
int all=0;
for(int i=1;i<=n;i++)
(all+=1LL*a[i]*b[i]%MOD)%=MOD;
int ans=1LL*legal*fmi(all,MOD-2)%MOD;
printf("%d\n",ans<0?ans+MOD:ans);
}
int main(){
sieve();
readData();
solve();
return 0;
}
?
?
【洛谷P4706】取石子