首页 > 其他分享 >CF1876G Clubstep

CF1876G Clubstep

时间:2024-10-17 19:48:10浏览次数:1  
标签:cur int sum CF1876G pos fa MAXN Clubstep

原题链接 CF1876G Clubstep

DX 上课讲的,有趣啊。

考虑暴力咋做。首先肯定不会选择一个 \(>r\) 的 \(p\) 来做操作,因为不如在 \(r\) 处做操作。那么一开始我们肯定要在 \(r\) 处做 \(\max(0,\lceil\dfrac{x-a_r}{2}\rceil)\) 次操作,然后接着往前做。

但是这样每次序列的值会变,发现在点 \(r\) 进行 \(k\) 次操作后,不修改 \([1,r)\) 的 \(a\) 值,而将 \(x\gets x-k\) 是等价的,于是便可以递归到 \([l,r)\) 的子问题。

考虑对于所有询问同时处理,从 \(n\) 扫到 \(1\),在扫到一个询问右端点时加入,左端点时删除。

维护一个优先队列,每次取出所有目前 \(x>a_i\) 的询问进行处理,然后再放回去。发现是否进行操作只与 \(x\) 值有关。

发现优先队列中相邻两个数的差值是 \(O(V)\) 的,当这两个询问同时进行操作后,它们的差值会折半,即 \(O(\log V)\) 次后这两个询问的 \(x\) 值将变得一样,可以合并起来。

也就是说设某次取出来 \(a\) 个数,其中就会有 \(a-1\) 个数与其前驱的差值折半,最多折半 \((O\log V)\) 次,所以最多总共进行 \(O(n+n\log V)\) 次操作。

问题在于维护答案,用带权并查集维护,可以将一个点的答案当做从其到其祖先的权值和,把 \(u\) 合并到 \(v\) 时将 \(f_u\gets f_u-f_v\)。再在路径压缩时记录从点 \(u\) 到其祖先(不含祖先)的权值和即可。或者直接启发式合并暴力跳也行,反正不是复杂度瓶颈。

总时间复杂度是 \(O(n\log nV)\) 的。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#define int long long
#define ll long long
using namespace std;
typedef pair<int,int> P;
const int MAXN=3e5+10;
int n,m,a[MAXN],fa[MAXN],x[MAXN],val[MAXN];
ll ans[MAXN],sum[MAXN];
vector <int> L[MAXN],R[MAXN];
priority_queue <P> q;vector <P> cur;
inline int find(int x)
{
    if(fa[x]==x) return x;
    if(fa[fa[x]]==fa[x]) return fa[x];
    int fax=find(fa[x]);
    sum[x]+=sum[fa[x]];return fa[x]=fax;
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n;for(int i=1;i<=n;++i) cin>>a[i];
    cin>>m;for(int i=1,l,r;i<=m;++i)
        cin>>l>>r>>x[i],fa[i]=i,
        L[l].push_back(i),R[r].push_back(i);
    for(int i=n;i;--i)
    {
        for(int p:R[i]) q.push({x[p],p});
        while(!q.empty()&&q.top().first>=a[i])
            cur.push_back(q.top()),q.pop();
        for(int j=cur.size()-1,pre=-1,num;~j;--j)
        {
            int x=cur[j].first,pos=cur[j].second;
            int k=(x-a[i]+1)/2;x-=k,sum[pos]+=k*i;
            if(pre!=-1&&x==num)
                fa[pos]=pre,sum[pos]-=sum[pre];
            else q.push({x,pos}),pre=pos,num=x;
        }
        cur.clear();
        for(int p:L[i])
        {
            find(p),ans[p]=sum[p];
            if(fa[p]!=p) ans[p]+=sum[fa[p]];
        }
    }
    for(int i=1;i<=m;++i) cout<<ans[i]<<'\n';
    return 0;
}

标签:cur,int,sum,CF1876G,pos,fa,MAXN,Clubstep
From: https://www.cnblogs.com/int-R/p/18472958/CF1876G

相关文章

  • Codeforces 1876G Clubstep.md
    首先考虑暴力的贪心。从\(r\)到\(l\)依次遍历,若\(a_i<x\)则一直进行题目中的操作。正确性是能保证的,因为选后面的\(j\)只能\(+1\),而选\(i\)可以\(+2\),且\(i\)前面的部分都是\(+1\)。考虑转化一下,把对\(i\)进行操作\(k\)次后,\(\forallj\in[1,i),a_j\l......