题解:P14009 「florr IO Round 1」数字游戏
随机跳题跳到的。虽有“两道题拼在一起”之嫌,但任然不失为一道好题。
看到这个定义,毫无疑问需要莫比乌斯反演,简单推一下式子。
全局和(不妨令 \(V= \max \{a_1,a_2,\cdots,a_n \}\))
该式子最后一项的
不好继续展开,考虑直接用数据结构维护。
这样我们扫描 \(d\),依次修改当 \(d\) 变化是会变化的 \(\left \lfloor \frac{a_i}{d}\right\rfloor\)。我们得到 \(\mathcal O(n \sqrt V)\) 次单点修改,\(\mathcal O(V)\) 次查询全局和。
维护结点 \([\mathrm{ans},\mathrm{pre},\mathrm{suf},\mathrm{prod}]\),其中 \(\mathrm{ans}\) 表示该区间答案 \(\displaystyle \sum _{i=l}^r \sum _{j=i} ^r \prod _{k=i} ^j \left \lfloor \frac{a_k}{d}\right\rfloor\),\(\mathrm{pre}\) 表示前缀积之和 \(\displaystyle \sum _{i=l} ^r \prod _{k=l} ^i \left \lfloor \frac{a_k}{d}\right\rfloor\),\(\mathrm{suf}\) 表示是区间后缀积之和 \(\displaystyle \sum_{i=l} ^r \prod _{k=i} ^r \left \lfloor \frac{a_k}{d}\right\rfloor\),\(\mathrm{prod}\) 表示区间积 \(\prod _{k=l} ^r \left \lfloor \frac{a_k}{d}\right\rfloor\)。合并两个区间为
简单分析一下可以知道,单位元为 \([0,0,0,1]\)。单独元素 \(x=\left \lfloor \frac{a_i}{d}\right\rfloor\) 构成 \([x,x,x,x]\)。满足结合律,不满足交换律,构成半群。
由于不满足交换律,似乎只能用线段树做到 \(\mathcal O(n \sqrt V \log n)\)。试了分块,没成功。求助得到 lxl 认为不可做。
考虑继续发掘题目性质。一点比较显然的是在 \(d\) 从 \(1\) 变化 \(\sqrt V\) 时有很多修改,几乎整棵线段树上每个结点都被修改了,这样的话如果能够“共用”从根到叶子的路径上的点可以大大降低复杂度了。而 \(d\) 从 \(\sqrt V\) 到 \(V\) 这一段修改相对“稀疏”。
所以我就有了如下的想法,每个结点额外维护 \(\mathrm{nxt}\) 表示该节点中下一次修改的 \(d\),如果递归到的结点 \(\mathrm{xnt}\) 比修改的 \(d\) 大就退出,否则直接暴力递归左右子树修改。大致写出这样的代码(其中的 w 直接表示 \([\mathrm{ans},\mathrm{pre},\mathrm{suf},\mathrm{prod}]\)):
inline void push_up(int p){
tr[p].nxt=min(tr[lc].nxt,tr[rc].nxt);
tr[p].w=tr[lc].w+tr[rc].w;
}
void upd(int p,int pl,int pr,int id){
if(tr[p].nxt>id) return;
if(pl==pr){
tr[p].w=answer(a[pl]/id);
if(a[pl]/id==0) tr[p].nxt=V+1;
else tr[p].nxt=a[pl]/(a[pl]/id)+1;
return;
}
assert(tr[p].nxt==id);
int mid=(pl+pr)>>1;
upd(lc,pl,mid,id);
upd(rc,mid+1,pr,id);
push_up(p);
}
乍一看复杂度任然是 \(\mathcal O (n \sqrt V \log n)\) 的,但仔细一想复杂度严格低于取 \(B\) 来平衡的官解(\(\le B\) 和 \(\gt B\) 的部分均为严格优,\(\le B\) 的部分会有一些无需重构的结点在此处被优化,\(\gt B\) 的部分会共用一部分结点)。
则复杂度在 \(\mathcal O(n \sqrt V)\) 到 \(\mathcal O(n \sqrt {V \log n})\) 之间,能否证到低于根号半 \(\log\) 的复杂度,我不大清楚。而且目前是最优解。
最后放一下完整的代码
#include <bits/stdc++.h>
using namespace std;
const int N=7e4+10;
const unsigned mod=998244353;
inline void addmod(unsigned &x,unsigned y){x+=y;(x>=mod)&&(x-=mod);}
inline void submod(unsigned &x,unsigned y){addmod(x,mod-y);}
inline unsigned plsmod(unsigned x,unsigned y){addmod(x,y);return x;}
inline unsigned minmod(unsigned x,unsigned y){return plsmod(x,mod-y);}
inline unsigned mulmod(unsigned x,unsigned y){return 1ull*x*y%mod;};
unsigned mu[N];
namespace Seive{
bool vis[N];
int pr[N/4];
void euler(){
int cnt=0;
for(int i=2;i<N;i++){
if(!vis[i]){
pr[++cnt]=i;
mu[i]=mod-1;
}
for(int j=1;pr[j]*i<N;j++){
vis[i*pr[j]]=1;
if(i%pr[j]==0){
mu[i*pr[j]]=0;
break;
}else{
mu[i*pr[j]]=mulmod(mu[i],mu[pr[j]]);
}
}
}
mu[1]=1;
}
}
int a[N];
unsigned ans;
vector<int> ds[N];
int n,V;
struct answer{
unsigned ans,suf,pre,prod;
answer():ans(0),suf(0),pre(0),prod(1){}
answer(int _a):ans(_a),suf(_a),pre(_a),prod(_a){}
answer(int _a,int _s,int _p,int _pp):ans(_a),suf(_s),pre(_p),prod(_pp){}
answer operator+(const answer o){
answer res;
res.ans=plsmod(ans,o.ans);
addmod(res.ans,mulmod(suf,o.pre));
res.suf=plsmod(mulmod(suf,o.prod),o.suf);
res.pre=plsmod(pre,mulmod(prod,o.pre));
res.prod=mulmod(prod,o.prod);
return res;
}
};
struct node{
answer w;
int nxt;//下一个需要修改的时刻
}tr[N*4];
#define lc (p<<1)
#define rc (p<<1|1)
inline void push_up(int p){
tr[p].nxt=min(tr[lc].nxt,tr[rc].nxt);
tr[p].w=tr[lc].w+tr[rc].w;
}
void build(int p,int pl,int pr){
if(pl==pr){
tr[p].w=answer(a[pl]);
tr[p].nxt=2;
return;
}
int mid=(pl+pr)>>1;
build(lc,pl,mid);
build(rc,mid+1,pr);
push_up(p);
}
void upd(int p,int pl,int pr,int id){
if(tr[p].nxt>id) return;
if(pl==pr){
tr[p].w=answer(a[pl]/id);
if(a[pl]/id==0) tr[p].nxt=V+1;
else tr[p].nxt=a[pl]/(a[pl]/id)+1;
return;
}
assert(tr[p].nxt==id);
int mid=(pl+pr)>>1;
upd(lc,pl,mid,id);
upd(rc,mid+1,pr,id);
push_up(p);
}
int main(){
Seive::euler();
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
V=max(V,a[i]);
}
build(1,1,n);
addmod(ans,tr[1].w.ans);
for(int d=2;d<=V;d++){
upd(1,1,n,d);
addmod(ans,mulmod(mu[d],tr[1].w.ans));
}
printf("%u\n",ans);
return 0;
}

浙公网安备 33010602011771号