郗燕岚 发表于 2025-6-9 19:31:26

[25BJWCB1A] 序列

题目大意

一个长度为 \(n\) 的序列 \(a\),与 \(m\) 次操作:
1.将 \(a_x\) 改成 \(y\)。
2. 求 \(\sum_{i=1}^x\sum_{i=1}^x f(i,j)\)
其中 \(f(i,j)\) 表示区间 \(\) 内的序列 \(a\) 的不同元素个数。
\(n,m\leq2\cdot10^5,a_i\leq n\)
思路

发现对于操作二直接做是不好维护的,于是这种题一般都可以转换成一个 \(a_i,i\in\) 对整个答案的贡献。考虑到包含同样元素的区间要求只被最左点记录一次,所以可以考虑维护一个 \(p_i\) 表示在 \(i\) 之前最后一个 \(a_i\) 出现的位置(特别的,如果没有出现记 \(p_i=0\))。
于是考虑到 \(a_i\),则左端点要从 \(p_i+1\) 开始,右端点可以一直到 \(x\) 因为我们要记录的是第一次出现的元素所做出贡献。可以有贡献 \((i-p_i)(x-i+1)\)。
拆开可以得到

\
发现求和后的话可以变成这样:

\
前面的可以用公式维护得到:

\[\frac{x^2(1+x)}{2}+\frac{x(x+1)(2x+1)}{6}-(x+1)\sum_{i=1}^xp_i+\sum_{i=1}^x ip_i\]
只用求 \(\sum_1^xp_i\) 和 \(\sum_1^x ip_i\) 即可。发现对于修改操作只会修改一个点,所以我们可以用 std::set 记录每一个元素的所有出现位置然后直接维护每一个 \(p_i\)。
那么对于那个式子就可以线段树单点修改区间查询来解决了。这个做法个人觉得比实现一个 \(p_i\) 的区间修改的其他奇怪容斥做法要好理解且好实现,不知道为什么大家都不直接计算贡献而是非要计算没有贡献的部分再去减。
但是对于某些将 \(a_x\) 改成 \(a_x\) 的特别无效操作要注意特判。
时间复杂度 \(O(n\log n)\)。
代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(register ll i=(a);i<=(b);++i)
#define endl '\n'
#define pfh(x) (x*(x+1)*(2*x+1)/6)
using namespace std;
typedef long long ll;
const ll MAXN=2e5+5;
ll n,m,a;
namespace Taskf{
        set<ll>se;
        struct node{
                ll sum;
                #define lc(u) (u<<1)
                #define rc(u) (u<<1|1)
        }t;
        void push_up(ll u){
                t.sum=t.sum+t.sum;
                t.sum=t.sum+t.sum;
        }
        void modify(ll u,ll l,ll r,ll x,ll val){
          if(x==n+1){
                return;
          }
                if(l==r){
                        t.sum=val;
                        t.sum=l*val;
                        return;
                }
                ll mid=(l+r)>>1;
                if(x<=mid){
                        modify(lc(u),l,mid,x,val);
                }else{
                        modify(rc(u),mid+1,r,x,val);
                }
                push_up(u);
        }
        ll query(ll u,ll l,ll r,ll ql,ll qr,ll id){
                if(ql<=l&&r<=qr){
                        return t.sum;
                }
                ll mid=(l+r)>>1,ans=0;
                if(ql<=mid){
                        ans+=query(lc(u),l,mid,ql,qr,id);
                }
                if(mid+1<=qr){
                        ans+=query(rc(u),mid+1,r,ql,qr,id);
                }
                return ans;
        }
        void Do(){
                rep(i,1,n){
                  if(!se].empty()){
                          modify(1,1,n,i,*(--se].end()));
                  }
                        se].insert(i);
                }
                rep(i,1,n){
                        se.insert(0);
                        se.insert(n+1);
                }
                rep(_,1,m){
                        ll op,x,y;
                        cin>>op>>x;
                        if(op==1){
                                cin>>y;
                                ll ny=*se.upper_bound(x);
                                ll py=(*(--se.lower_bound(x)));
                                ll nx=*se].upper_bound(x);
                                ll px=(*(--se].lower_bound(x)));
                                modify(1,1,n,ny,x);
                                modify(1,1,n,x,py);
                                if(a!=y){
                                  modify(1,1,n,nx,px);
                                }
                                se].erase(x);
                                a=y;
                                se.insert(x);
                        }else{
                                ll yituo=-pfh(x)+(x+1)*(x+1)*x/2;
                                ll f=(x+1)*query(1,1,n,1,x,0),g=query(1,1,n,1,x,1);
                                cout<<yituo-f+g<<endl;
                        }
                }
        }
}
int main(){
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        cin>>n>>m;
        rep(i,1,n){
                cin>>a;
        }
        Taskf::Do();
        return 0;
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: [25BJWCB1A] 序列