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
 */
posted @ 2022-10-17 16:06  Day_Dreamer_D  阅读(285)  评论(0)    收藏  举报