Ynoi 题解
0. 洛谷 P3987 我永远喜欢珂朵莉~
算法:树状数组。
模型抽象
Trick:$\forall k \ge 2$,最多 $\log a_i$ 次 $a_i \div k$ 使得 $a_i=1$。
证明:每次最少会使 $a_i$ 减半,最多 $\log$ 次就可以变为 $1$。
所以修改的复杂度最多 $O(n\log a_i)$,把单次查询的复杂度控制在 $O(\log n)$ 就行,于是我们考虑树状数组。
具体实现
细想会发现,BIT 做法的难点在于找需要被修改的数。
参考 @蒟蒻zyh 的题解,我们可以想到用 vector 记录下第 $i$ 个包含因数 $i$ 的元素的下标,配合 STL 中的 upper_bound 和 lower_bound 函数,可以找到修改的起讫点,遍历其中的元素单点在 BIT 中修改即可。
注意一个实现的细节,vector 在 erase 后,后面元素的下标会集体减一,所以务必从后向前 erase,这样前一次删除操作不会影响后面的数。
Code
//P3987 我永远喜欢珂朵莉~
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int>::iterator iter;
const int MAXN=100010;
int n,m,a[MAXN];
ll tr[MAXN];
vector<int> sub[MAXN*5];
vector<iter> it;
namespace FASTIO
{
inline int read()
{
int res=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(isdigit(ch))
{
res=res*10+ch-'0';
ch=getchar();
}
return res*f;
}
inline void write(ll x)
{
int top=0;
char s[25];
if(x<0)
{
putchar('-');
x=-x;
}
do
{
s[++top]=x%10+'0';
x/=10;
}
while(x);
while(top)
putchar(s[top--]);
return;
}
}
using namespace FASTIO;
namespace BIT
{
#define lowbit(x) ((x)&-(x))
inline void add(int x,int c)
{
for(register int i=x;i<=n;i+=lowbit(i))
{
tr[i]+=c;
}
return;
}
inline ll sum(int x)
{
ll res=0;
for(register int i=x;i;i-=lowbit(i))
{
res+=tr[i];
}
return res;
}
}
using namespace BIT;
int main()
{
int op,l,r,x;
iter u,v;
n=read();
m=read();
for(register int i=1;i<=n;i++)
{
a[i]=read();
for(register int j=1;j*j<=a[i];j++)
{
if(a[i]%j==0)
{
sub[j].emplace_back(i);//push_back 的替代品
if(j*j!=a[i])
{
sub[a[i]/j].emplace_back(i);
}
}
}
add(i,a[i]);
}
while(m--)
{
op=read();
if(op==1)
{
l=read();
r=read();
x=read();
it.clear();
if(x==1||sub[x].empty())
continue;
u=lower_bound(sub[x].begin(),sub[x].end(),l);
v=upper_bound(sub[x].begin(),sub[x].end(),r);
if(u==sub[x].end())
continue;
for(iter w=u;w!=v;w++)
{
if(a[*w]%x)
continue;
add(*w,-(a[*w]-a[*w]/x));
a[*w]/=x;
if(a[*w]%x)
it.emplace_back(w);
}
if(!it.empty())
{
for(int i=it.size()-1;~i;i--)
{
sub[x].erase(it[i]);
}
}
}
else
{
l=read();
r=read();
write(sum(r)-sum(l-1));
putchar('\n');
}
}
return 0;
}
/*
* 洛谷
* https://www.luogu.com.cn/problem/P3987
* C++23 -O2
* 2022.10.17
*/
1. 洛谷 P5610 [Ynoi2013] 大学
不难看出,这题是上一题的加强版,加强在于:
- 强制在线。
- 时间限制由 4000ms 变为 500ms。
我们可以尝试优化上一道题的做法。
不难发现,上一个题的程序的瓶颈在于 vector 的 erase,于是我们需要一种高效的数据结构来维护这个桶,这个数据结构需要支持单点查询、单点删除和 lower_bound。
这当然可以用平衡树实现,但是由于这题过于卡常(500ms),所以我们考虑维护每个元素后面(含元素自身)第一个没被删除的数 $fa_i$。
不难发现它有如下性质:
- 初始时 $fa_i=i$。
- $fa_i=fa_{fa_i}$。
这不就是并查集吗?
至此,我们解决了 vector erase 操作常数过大的问题。
然而,仅仅这样还是会 TLE。
因为这是 Ynoi,所以常数卡得很死,vector 的 push_back 操作被卡掉了(其本身也有额外的时空消耗,emplace_back 只能减小一点)。
不得已,我们采取手写内存池的方式解决。
最终单点最大用时 477ms,总用时 6.48s。
Code
//P5610 [Ynoi2013] 大学
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=100010,MAXM=500010;
struct IO{/*省略快读*/}io;
int n,m,mx,a[MAXN],o[MAXM],memPool[233333333],*bin[MAXM],*pool=memPool,*fa[MAXM];
ll tr[MAXN],ans;
namespace BIT
{
#define lowbit(x) ((x)&-(x))
inline void add(int x,int c)
{
for(register int i=x;i<=n;i+=lowbit(i))
{
tr[i]+=c;
}
return;
}
inline ll sum(int x)
{
ll res=0;
for(register int i=x;i;i-=lowbit(i))
{
res+=tr[i];
}
return res;
}
}
using namespace BIT;
namespace UFS
{
int find(const int &x,const int &i)
{
if(i==o[x]||i==fa[x][i])
return i;
return fa[x][i]=find(x,fa[x][i]);
}
}
using namespace UFS;
int main()
{
int op,l,r,x;
io>>n>>m;
for(register int i=1;i<=n;i++)
{
io>>a[i];
add(i,a[i]);
mx=max(mx,a[i]);
o[a[i]]++;
}
for(register int i=1;i<=mx;i++)
for(register int j=i<<1;j<=mx;j+=i)
o[i]+=o[j];
for(register int i=1;i<=mx;i++)
{
bin[i]=pool,pool+=o[i];
fa[i]=pool,pool+=o[i];
for(register int j=0;j<o[i];j++)
{
fa[i][j]=j;
}
o[i]=0;
}
for(register int i=1;i<=n;i++)
{
for(register int j=1;j*j<=a[i];j++)
{
if(a[i]%j==0)
{
bin[j][o[j]++]=i;
if(a[i]!=j*j)
bin[a[i]/j][o[a[i]/j]++]=i;
}
}
}
for(register int i=1;i<=m;i++)
{
io>>op;
if(op==1)
{
io>>l>>r>>x;
l^=ans,r^=ans,x^=ans;
if(o[x]==0||x==1)
{
continue;
}
for(register int i=find(x,lower_bound(bin[x],bin[x]+o[x],l)-bin[x]);i<o[x]&&bin[x][i]<=r;i=find(x,i+1))
{
if(a[bin[x][i]]%x==0)
{
add(bin[x][i],-a[bin[x][i]]+a[bin[x][i]]/x);
a[bin[x][i]]/=x;
}
if(a[bin[x][i]]%x)
fa[x][i]=i+1;
}
}
else
{
io>>l>>r;
l^=ans,r^=ans;
io<<(ans=sum(r)-sum(l-1))<<'\n';
}
}
return 0;
}
/*
* 洛谷
* https://www.luogu.com.cn/problem/P5610
* C++23 -O2
* 2022.10.17
*/

浙公网安备 33010602011771号