これは割と定番かも。
https://yukicoder.me/problems/no/2075
問題
整数列Aが与えられる。
ここから部分列を取ったとき、隣接要素同士が互いに素でないような物は何通りか。
解法
約数包除の要領で解く。
まず各A[i]について、素因数分解したときの次数が2以上であることは互いに素の判定に関係ないので、次数はすべて1であるとみなしてよい。
dp(n) := Aを先頭から見て行って、条件を満たすよう部分列を抽出していったとき、末尾がnの倍数であるものの組み合わせ数
末尾にA[i]を取れるケースはA[i]の各約数dに対し、dp(d)を素因数の偶奇に合わせて足し引きした結果である。
また、末尾にA[i]を追加した分、上記結果を再びdp(d)へ加算しよう。
最終的に、dp(n)*f(n) (f(n)はnの素因数の偶奇により+1/-1を取る)の総和が解。
ll N; ll dp[1000001]; int pat[1010101]; const ll mo=998244353; set<ll> enumpr(ll n) { set<ll> V; for(ll i=2;i*i<=n;i++) while(n%i==0) V.insert(i),n/=i; if(n>1) V.insert(n); return V; } void solve() { int i,j,k,l,r,x,y; string s; cin>>N; ll ret=0; while(N--) { cin>>x; if(x==1) { ret++; continue; } set<ll> S=enumpr(x); vector<int> V; FORR(s,S) V.push_back(s); ll ret=1; int M=V.size(); for(int mask=0;mask<1<<M;mask++) if(mask) { int v=-1; FOR(i,M) if(mask&(1<<i)) v=-v*V[i]; if(v>0) { ret+=dp[v]; pat[v]=1; } else { ret+=mo-dp[-v]; pat[-v]=-1; } } ret%=mo; for(int mask=0;mask<1<<M;mask++) if(mask) { int v=1; FOR(i,M) if(mask&(1<<i)) v=v*V[i]; (dp[v]+=ret)%=mo; } } FOR(i,1000001) ret+=dp[i]*pat[i]; cout<<(ret%mo+mo)%mo<<endl; }
まとめ
こういうのもっと短く書けないかな…。