peiwenjun's blog 没有知识的荒原

P7722 [Ynoi2007] tmpq 题解

题目描述

给定长为 \(n\) 的数组 \(a,b,c\) ,接下来 \(m\) 次操作:

  • 1 x y :将 \(a_x\) 改为 \(y\)
  • 2 x :求 \(\sum\limits_{1\le i\lt j\lt k\le x}[b_{a_i}=a_j=c_{a_k}]\)

数据范围

  • \(1\le n\le 2\cdot 10^5,1\le m\le 5\cdot 10^4,1\le a_i,b_i,c_i,x,y\le n\)

时间限制 \(\texttt{4s}\) ,空间限制 \(\texttt{64MB}\)

分析

问题等价于每次对 \(a,b,c\) 单点修改,查询 \(\sum\limits_{1\le i\lt j\lt k\le x}[b_i=a_j=c_k]\)

离线所有操作,记录每个数在 \(a,b,c\) 中不同出现位置个数,并对此进行根号分治。

对于出现次数 \(\le B\) 的所有数 \(x\) ,可以用 \(\mathcal O(cnt_x)\) 的代价计算这个数的贡献。

具体的, \(f_{i,0/1/2}\) 表示考虑前 \(i\) 个位置,当前阶段为 \(b/a/c\) 的方案数,最后把 \(f_{i,2}\) 加入全局贡献即可。

修改时先撤销原本的贡献,重新跑 \(\texttt{dp}\) 然后加入新的贡献,要求支持 \(\mathcal O(n+mB)\) 次单点修改和 \(\mathcal O(m)\) 次查询前缀和,可以用分块平衡。

对于出现次数 \(\ge B\) 的所有数 \(x\) ,这样的数仅有 \(\frac{n+3m}B\) 个。

类比动态 \(\texttt{dp}\) ,用矩阵刻画转移,要求支持 \(\mathcal O(m)\) 次单点修改矩阵和 \(\mathcal O(m\frac{n+m}B)\) 次查询前缀积,也可以用分块平衡。

我们无法开下 \(\mathcal O(m\frac{n+m}B)\) 的空间,因此不能同时维护所有数的矩阵。

但是注意到可以离线,枚举每个 \(cnt_x\gt B\) 的数然后跑所有询问,可以做到空间线性。

时间复杂度 \(\mathcal O(mB+m\frac{n+m}B)=\mathcal O(m\sqrt{n+m})\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,B=600;
int m,n;
int a[maxn],b[maxn],c[maxn],_a[maxn],_b[maxn],_c[maxn];
int bel[maxn],st[maxn],ed[maxn];
ll res[maxn];
vector<int> pos[maxn];
struct oper
{
    int op,x,y;
}q[maxn];
inline int read()
{
    int q=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) q=10*q+ch-'0',ch=getchar();
    return q;
}
namespace block1
{
    ll f[B+5][2],cur[maxn],sum[maxn];
    inline void add(int x,ll v)
    {
        cur[x]+=v,sum[bel[x]]+=v;
    }
    inline ll query(int x)
    {
        ll res=0;
        for(int i=1;i<bel[x];i++) res+=sum[i];
        for(int i=st[bel[x]];i<=x;i++) res+=cur[i];
        return res;
    }
    inline void upd(int x,int op)
    {
        if(pos[x].size()>B) return ;
        for(int i=1;i<pos[x].size();i++)
        {
            f[i][0]=f[i-1][0]+(b[pos[x][i]]==x);
            f[i][1]=f[i-1][1]+(a[pos[x][i]]==x?f[i-1][0]:0);
            if(c[pos[x][i]]==x) block1::add(pos[x][i],op*f[i-1][1]);
        }
    }
}
namespace block2
{
    int u;
    struct mat
    {
        ll a,b,c,d,e,f;
    }cur[maxn],sum[maxn];
    inline mat operator*(mat x,mat y)
    {
        return {y.a+x.a,y.b+x.a*y.d+x.b,y.c+x.a*y.e+x.b*y.f+x.c,y.d+x.d,y.e+x.d*y.f+x.e,y.f+x.f};
    }
    inline mat get(int x)
    {
        return {b[x]==u,0,0,a[x]==u,0,c[x]==u};
    }
    inline void upd(int x)
    {
        for(int i=x;i<=ed[bel[x]];i++) cur[i]=i!=st[bel[x]]?cur[i-1]*get(i):get(i);
        for(int i=bel[x];i<=bel[n];i++) sum[i]=sum[i-1]*cur[ed[i]];
    }
    inline ll query(int x)
    {
        return (sum[bel[x]-1]*cur[x]).c;
    }
    inline void solve(int _u)
    {
        if(pos[u=_u].size()<=B) return ;
        for(int i=1;i<=n;i++) a[i]=_a[i],b[i]=_b[a[i]],c[i]=_c[a[i]];
        for(int i=1;i<=n;i++) cur[i]=i!=st[bel[i]]?cur[i-1]*get(i):get(i);
        for(int i=1;i<=bel[n];i++) sum[i]=sum[i-1]*cur[ed[i]];
        for(int i=1;i<=m;i++)
        {
            int x=q[i].x,y=q[i].y;
            if(q[i].op==1)
            {
                int flg=a[x]==u||b[x]==u||c[x]==u||y==u||_b[y]==u||_c[y]==u;
                a[x]=y,b[x]=_b[y],c[x]=_c[y];
                if(flg) upd(x);
            }
            else res[i]+=query(x);
        }
    }
}
using block1::upd;
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) _a[i]=read();
    for(int i=1;i<=n;i++) _b[i]=read();
    for(int i=1;i<=n;i++) _c[i]=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=_a[i],b[i]=_b[a[i]],c[i]=_c[a[i]];
        pos[a[i]].push_back(i),pos[b[i]].push_back(i),pos[c[i]].push_back(i);
    }
    for(int i=1;i<=m;i++)
    {
        q[i].op=read(),q[i].x=read();
        if(q[i].op==1)
        {
            q[i].y=read();
            int x=q[i].x,y=q[i].y;
            pos[y].push_back(x),pos[_b[y]].push_back(x),pos[_c[y]].push_back(x);
        }
    }
    for(int i=1;i<=n;i++) bel[i]=(i-1)/B+1;
    for(int i=1;i<=bel[n];i++) st[i]=(i-1)*B+1,ed[i]=min(i*B,n);
    for(int i=1;i<=n;i++)
    {
        auto &vec=pos[i];
        vec.push_back(0),sort(vec.begin(),vec.end());
        vec.erase(unique(vec.begin(),vec.end()),vec.end());
        upd(i,1);
    }
    for(int i=1;i<=m;i++)
    {
        int x=q[i].x,y=q[i].y;
        if(q[i].op==1)
        {
            set<int> tmp={a[x],b[x],c[x],y,_b[y],_c[y]};
            for(auto x:tmp) upd(x,-1);
            a[x]=y,b[x]=_b[y],c[x]=_c[y];
            for(auto x:tmp) upd(x,1);
        }
        else res[i]=block1::query(x);
    }
    for(int i=1;i<=n;i++) block2::solve(i);
    for(int i=1;i<=m;i++) if(q[i].op==2) printf("%lld\n",res[i]);
    return 0;
}

posted on 2023-07-07 19:54  peiwenjun  阅读(9)  评论(0)    收藏  举报

导航