题解 调兵遣将
不会做
考虑一个暴力扫描线,会发现对于一个右端点,区间 gcd 不同的左端点形成了 log 个区间
然后发现题目要求选出的所有区间 gcd 都相等,所以尝试将 gcd 相等的区间都拎出来
发现因为需要考虑让第 \(i\) 个点在这个区间里所以并不好处理
那么进行补集转化,求第 \(i\) 个点不在任何一个选出的区间的方案数
那么(对于每个 gcd)令 \(f_{i}\) 为在 \([1, i]\) 中选出若干区间 gcd 为 当前枚举的值的方案数,\(g_i\) 为 \([i, n]\) 中的
考虑刚才拎出的区间是形如 \((l, r, R)\) 表示 \(L\in[l, r]\) 时,\(\gcd_{l, r}=当前值\)
那么对 \(f\) 的贡献就是 \(\forall i\in[R, n], f_i\gets f_i+\sum\limits_{j=l}^rf_{j-1}\),g 同理
然后发现对于不是区间关键点的点形成的连续段,当前 gcd 对这个连续段中的每个点贡献相同
那么可以整体用线段树维护
复杂度 \(O(n\log n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 5000010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int a[N];
const ll mod=998244353;
inline int gcd(int a, int b) {return !b?a:gcd(b, a%b);}
// namespace force{
// ll ans[N], bit[N];
// inline void add(int i, ll dat) {for (; i<=n; i+=i&-i) bit[i]=(bit[i]+dat)%mod;}
// inline ll query(int i) {ll ans=0; for (; i; i-=i&-i) ans=(ans+bit[i])%mod; return ans;}
// struct pst{
// #define ls(p) lson[p]
// #define rs(p) rson[p]
// int lson[N*800], rson[N*800], val[N*800], rot[N], tot;
// void upd(int& p1, int p2, int tl, int tr, int pos, ll dat) {
// p1=++tot;
// if (tl==tr) {val[p1]=(val[p2]+dat)%mod; return; }
// int mid=(tl+tr)>>1;
// if (pos<=mid) upd(ls(p1), ls(p2), tl, mid, pos, dat), rs(p1)=rs(p2);
// else upd(rs(p1), rs(p2), mid+1, tr, pos, dat), ls(p1)=ls(p2);
// }
// ll query(int p, int tl, int tr, int pos) {
// if (!p) return 0;
// if (tl==tr) return val[p];
// int mid=(tl+tr)>>1;
// if (pos<=mid) return query(ls(p), tl, mid, pos);
// else return query(rs(p), mid+1, tr, pos);
// }
// void cpy(int tim1, int tim2) {rot[tim1]=rot[tim2];}
// void upd(int tim1, int tim2, int pos, ll val) {upd(rot[tim1], rot[tim2], 1, 1e9, pos, val);}
// ll query(int tim, int pos) {return query(rot[tim], 1, 1e9, pos);}
// }tr1, tr2;
// void solve() {
// // cout<<double(sizeof(tr1)*2)/1000/1000<<endl; exit(0);
// for (int i=1; i<=n; ++i) {
// int now=a[i]; tr1.cpy(i, i-1);
// for (int j=i; j; --j) {
// now=gcd(now, a[j]);
// // cout<<"addl: "<<j<<' '<<i<<' '<<tr1.query(j-1, now)<<endl;
// tr1.upd(i, i, now, tr1.query(j-1, now)+1);
// }
// }
// for (int i=n; i; --i) {
// int now=a[i]; tr2.cpy(i, i+1);
// for (int j=i; j<=n; ++j) {
// now=gcd(now, a[j]);
// tr2.upd(i, i, now, tr2.query(j+1, now)+1);
// }
// }
// for (int i=1; i<=n; ++i) ans[i]=1;
// for (int i=1; i<=n; ++i) {
// int now=a[i];
// for (int j=i; j<=n; ++j) {
// now=gcd(now, a[j]);
// ll tem=(tr1.query(i-1, now)+1)*(tr2.query(j+1, now)+1)%mod;
// add(i, tem); add(j+1, -tem);
// }
// }
// for (int i=1; i<=n; ++i) printf("%lld ", (query(i)%mod+mod)%mod);
// printf("\n");
// }
// }
// namespace task1{
// ll ans[N], bit[N];
// inline void add(int i, ll dat) {for (; i<=n; i+=i&-i) bit[i]=(bit[i]+dat)%mod;}
// inline ll query(int i) {ll ans=0; for (; i; i-=i&-i) ans=(ans+bit[i])%mod; return ans;}
// struct pst{
// #define ls(p) lson[p]
// #define rs(p) rson[p]
// int lson[N*800], rson[N*800], val[N*800], rot[N], tot;
// void upd(int& p1, int p2, int tl, int tr, int pos, ll dat) {
// p1=++tot;
// if (tl==tr) {val[p1]=(val[p2]+dat)%mod; return; }
// int mid=(tl+tr)>>1;
// if (pos<=mid) upd(ls(p1), ls(p2), tl, mid, pos, dat), rs(p1)=rs(p2);
// else upd(rs(p1), rs(p2), mid+1, tr, pos, dat), ls(p1)=ls(p2);
// }
// ll query(int p, int tl, int tr, int pos) {
// if (!p) return 0;
// if (tl==tr) return val[p];
// int mid=(tl+tr)>>1;
// if (pos<=mid) return query(ls(p), tl, mid, pos);
// else return query(rs(p), mid+1, tr, pos);
// }
// void cpy(int tim1, int tim2) {rot[tim1]=rot[tim2];}
// void upd(int tim1, int tim2, int pos, ll val) {upd(rot[tim1], rot[tim2], 1, 1e9, pos, val);}
// ll query(int tim, int pos) {return query(rot[tim], 1, 1e9, pos);}
// }tr1, tr2;
// void solve() {
// // cout<<double(sizeof(tr1)*2)/1000/1000<<endl; exit(0);
// for (int i=1; i<=n; ++i) {
// int now=a[i]; tr1.cpy(i, i-1);
// ll sum=0;
// for (int j=i; j; --j) {
// if (now!=gcd(now, a[j])) tr1.upd(i, i, now, sum), sum=0;
// now=gcd(now, a[j]);
// // cout<<"addl: "<<j<<' '<<i<<' '<<tr1.query(j-1, now)<<endl;
// // tr1.upd(i, i, now, tr1.query(j-1, now)+1);
// sum=(sum+tr1.query(j-1, now)+1)%mod;
// }
// tr1.upd(i, i, now, sum);
// }
// for (int i=n; i; --i) {
// int now=a[i]; tr2.cpy(i, i+1);
// ll sum=0;
// for (int j=i; j<=n; ++j) {
// if (now!=gcd(now, a[j])) tr2.upd(i, i, now, sum), sum=0;
// now=gcd(now, a[j]);
// // tr2.upd(i, i, now, tr2.query(j+1, now)+1);
// sum=(sum+tr2.query(j+1, now)+1)%mod;
// }
// tr2.upd(i, i, now, sum);
// }
// for (int i=1; i<=n; ++i) ans[i]=1;
// for (int i=1; i<=n; ++i) {
// int now=a[i];
// for (int j=i; j<=n; ++j) {
// now=gcd(now, a[j]);
// ll tem=(tr1.query(i-1, now)+1)*(tr2.query(j+1, now)+1)%mod;
// add(i, tem); add(j+1, -tem);
// }
// }
// for (int i=1; i<=n; ++i) printf("%lld ", (query(i)%mod+mod)%mod);
// printf("\n");
// }
// }
namespace task2{
ll f[N], g[N], ans[N];
int st[21][N], lg[N], uni[N], usiz;
struct range{int l, r, pos, w;};
vector<range> left, right, sta1[N], sta2[N], sta;
inline bool operator < (range a, range b) {return a.pos<b.pos;}
inline int qgcd(int l, int r) {int t=lg[r-l+1]-1; return gcd(st[t][l], st[t][r-(1<<t)+1]);}
// inline int qgcd(int l, int r) {int ans=a[l]; for (int i=l+1; i<=r; ++i) ans=gcd(ans, a[i]); return ans;}
void solve() {
for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
int t=lg[n]-1;
for (int i=1; i<=n; ++i) st[0][i]=a[i];
for (int i=1; i<=t; ++i) {
for (int j=1,len; j+(1<<i)-1<=n; ++j) {
len=1<<i-1;
st[i][j]=gcd(st[i-1][j], st[i-1][j+len]);
}
}
for (int i=1; i<=n; ++i) {
// cout<<"i: "<<i<<endl;
for (int now=i, lst=i; now; lst=--now) {
int l=1, r=lst, mid;
while (l<=r) {
mid=(l+r)>>1;
if (qgcd(mid, i)==qgcd(lst, i)) r=mid-1;
else l=mid+1;
}
now=r+1;
// cout<<"("<<now<<','<<lst<<','<<i<<','<<qgcd(lst, i)<<")"<<endl;
left.pb({now, lst, i, uni[++usiz]=qgcd(lst, i)});
}
}
for (int i=n; i; --i) {
for (int now=i, lst=i; now<=n; lst=++now) {
int l=lst, r=n, mid;
while (l<=r) {
mid=(l+r)>>1;
if (qgcd(i, mid)==qgcd(i, lst)) l=mid+1;
else r=mid-1;
}
now=l-1;
right.pb({lst, now, i, uni[++usiz]=qgcd(i, lst)});
}
}
sort(uni+1, uni+usiz+1);
usiz=unique(uni+1, uni+usiz+1)-uni-1;
for (auto& it:left) sta1[lower_bound(uni+1, uni+usiz+1, it.w)-uni].pb(it);
for (auto& it:right) sta2[lower_bound(uni+1, uni+usiz+1, it.w)-uni].pb(it);
for (int i=1; i<=usiz; ++i) {
for (int j=0; j<=n+1; ++j) f[j]=g[j]=1;
sort(sta1[i].begin(), sta1[i].end());
sort(sta2[i].begin(), sta2[i].end());
reverse(sta2[i].begin(), sta2[i].end());
for (auto it:sta1[i]) {
ll tem=0;
for (int j=it.l; j<=it.r; ++j) tem=(tem+f[j-1])%mod;
for (int j=it.pos; j<=n; ++j) f[j]=(f[j]+tem)%mod;
}
for (auto it:sta2[i]) {
ll tem=0;
for (int j=it.l; j<=it.r; ++j) tem=(tem+g[j+1])%mod;
for (int j=it.pos; j; --j) g[j]=(g[j]+tem)%mod;
}
for (int j=1; j<=n; ++j) ans[j]=(ans[j]+f[n]-f[j-1]*g[j+1])%mod;
}
for (int i=1; i<=n; ++i) printf("%lld%c", (ans[i]%mod+mod)%mod, " \n"[i==n]);
}
}
namespace task{
// ll f[N], g[N], ans[N];
int st[21][N], lg[N], uni[N], sta[N], usiz, top;
struct range{int l, r, pos, w;};
vector<range> left, right, sta1[N], sta2[N];
inline bool operator < (range a, range b) {return a.pos<b.pos;}
inline int qgcd(int l, int r) {int t=lg[r-l+1]-1; return gcd(st[t][l], st[t][r-(1<<t)+1]);}
// inline int qgcd(int l, int r) {int ans=a[l]; for (int i=l+1; i<=r; ++i) ans=gcd(ans, a[i]); return ans;}
struct seg{
#define tl(p) tl[p]
#define tr(p) tr[p]
#define pushup(p) sum[p]=(sum[p<<1]+sum[p<<1|1])%mod
bool clr[N];
int tl[N], tr[N];
ll sum[N], tag[N], len[N];
inline void spread(int p) {
if (clr[p]) {
sum[p<<1]=len[p<<1]; tag[p<<1]=0; clr[p<<1]=1;
sum[p<<1|1]=len[p<<1|1]; tag[p<<1|1]=0; clr[p<<1|1]=1;
clr[p]=0;
}
if (tag[p]) {
sum[p<<1]=(sum[p<<1]+tag[p]*len[p<<1])%mod; tag[p<<1]=(tag[p<<1]+tag[p])%mod;
sum[p<<1|1]=(sum[p<<1|1]+tag[p]*len[p<<1|1])%mod; tag[p<<1|1]=(tag[p<<1|1]+tag[p])%mod;
tag[p]=0;
}
}
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r; len[p]=r-l+1;
if (tl(p)==tr(p)) return ;
int mid=(tl(p)+tr(p))>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
}
void upd(int p, int l, int r, ll val) {
if (l<=tl(p)&&r>=tr(p)) {sum[p]=(sum[p]+val*len[p])%mod; tag[p]=(tag[p]+val)%mod; return ;}
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (l<=mid) upd(p<<1, l, r, val);
if (r>mid) upd(p<<1|1, l, r, val);
pushup(p);
}
ll query(int p, int l, int r) {
if (l<=tl(p)&&r>=tr(p)) return sum[p];
spread(p);
int mid=(tl(p)+tr(p))>>1; ll ans=0;
if (l<=mid) ans=(ans+query(p<<1, l, r))%mod;
if (r>mid) ans=(ans+query(p<<1|1, l, r))%mod;
return ans;
}
void set() {sum[1]=len[1]; tag[1]=0; clr[1]=1;}
}f, g, ans;
void solve() {
f.build(1, 0, n+1); g.build(1, 0, n+1); ans.build(1, 0, n+1);
for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
int t=lg[n]-1;
for (int i=1; i<=n; ++i) st[0][i]=a[i];
for (int i=1; i<=t; ++i) {
for (int j=1,len; j+(1<<i)-1<=n; ++j) {
len=1<<i-1;
st[i][j]=gcd(st[i-1][j], st[i-1][j+len]);
}
}
for (int i=1; i<=n; ++i) {
// cout<<"i: "<<i<<endl;
for (int now=i, lst=i; now; lst=--now) {
int l=1, r=lst, mid;
while (l<=r) {
mid=(l+r)>>1;
if (qgcd(mid, i)==qgcd(lst, i)) r=mid-1;
else l=mid+1;
}
now=r+1;
// cout<<"("<<now<<','<<lst<<','<<i<<','<<qgcd(lst, i)<<")"<<endl;
left.pb({now, lst, i, uni[++usiz]=qgcd(lst, i)});
}
}
for (int i=n; i; --i) {
for (int now=i, lst=i; now<=n; lst=++now) {
int l=lst, r=n, mid;
while (l<=r) {
mid=(l+r)>>1;
if (qgcd(i, mid)==qgcd(i, lst)) l=mid+1;
else r=mid-1;
}
now=l-1;
right.pb({lst, now, i, uni[++usiz]=qgcd(i, lst)});
}
}
sort(uni+1, uni+usiz+1);
usiz=unique(uni+1, uni+usiz+1)-uni-1;
for (auto& it:left) sta1[lower_bound(uni+1, uni+usiz+1, it.w)-uni].pb(it);
for (auto& it:right) sta2[lower_bound(uni+1, uni+usiz+1, it.w)-uni].pb(it);
for (int i=1; i<=usiz; ++i) {
// cout<<"i: "<<i<<endl;
sta[1]=1; sta[top=2]=n;
// for (int j=0; j<=n+1; ++j) f[j]=g[j]=1;
f.set(); g.set();
sort(sta1[i].begin(), sta1[i].end());
sort(sta2[i].begin(), sta2[i].end());
reverse(sta2[i].begin(), sta2[i].end());
for (auto it:sta1[i]) {
sta[++top]=it.pos;
// ll tem=0;
// for (int j=it.l; j<=it.r; ++j) tem=(tem+f[j-1])%mod;
// for (int j=it.pos; j<=n; ++j) f[j]=(f[j]+tem)%mod;
f.upd(1, it.pos, n, f.query(1, it.l-1, it.r-1));
}
for (auto it:sta2[i]) {
sta[++top]=it.pos;
// ll tem=0;
// for (int j=it.l; j<=it.r; ++j) tem=(tem+g[j+1])%mod;
// for (int j=it.pos; j; --j) g[j]=(g[j]+tem)%mod;
g.upd(1, 1, it.pos, g.query(1, it.l+1, it.r+1));
}
// for (int j=1; j<=n; ++j) ans[j]=(ans[j]+f[n]-f[j-1]*g[j+1])%mod;
sort(sta+1, sta+top+1);
top=unique(sta+1, sta+top+1)-sta-1;
// cout<<"f: "; for (int j=1; j<=n; ++j) cout<<f[j]<<' '; cout<<endl;
// cout<<"g: "; for (int j=1; j<=n; ++j) cout<<g[j]<<' '; cout<<endl;
// cout<<"sta: "; for (int j=1; j<=n; ++j) cout<<sta[j]<<' '; cout<<endl;
// for (int j=1; j<=top; ++j) ans[sta[j]]=(ans[sta[j]]+f[n]-f[sta[j]-1]*g[sta[j]+1])%mod;
ll fn=f.query(1, n, n);
for (int j=1; j<=top; ++j) ans.upd(1, sta[j], sta[j], (fn-f.query(1, sta[j]-1, sta[j]-1)*g.query(1, sta[j]+1, sta[j]+1))%mod);
// for (int j=1; j<top; ++j)
// for (int k=sta[j]+1; k<sta[j+1]; ++k)
// ans[k]=(ans[k]+f[n]-f[sta[j]]*g[sta[j+1]])%mod;
for (int j=1; j<top; ++j) if (sta[j]+1<=sta[j+1]-1)
ans.upd(1, sta[j]+1, sta[j+1]-1, (fn-f.query(1, sta[j], sta[j])*g.query(1, sta[j+1], sta[j+1]))%mod);
}
for (int i=1; i<=n; ++i) printf("%lld%c", (ans.query(1, i, i)%mod+mod)%mod, " \n"[i==n]);
}
}
signed main()
{
n=read();
for (int i=1; i<=n; ++i) a[i]=read();
// force::solve();
task::solve();
return 0;
}