题解 P8936【[JRKSJ R7] 月下缭乱】

Link

题意

你有一个长度为 n,初值全为 0 的序列 a

你有长为 m 的操作序列,其中第 i 次有三个参数 li,ri,xi,表示令 j[li,ri],ajmax(aj,xi)

sol(l,r) 表示依次操作第 l 至第 r 个操作后的 a 序列。

你需要回答有多少对 (l,r) 满足 1lrmsol(l,r)=sol(1,m)

1n,m106

题解

n,m3×105

与正解无关。这一部分分是口胡的,不知道对不对/能不能优化。

对每个位置维护一个 multiset,对于一次操作,将 xi 插入到 liri 的集合中。线段树维护。一个结点代表的区间的最大值就是其到根的路径上所有结点维护的集合的最大值的最大值。

考虑双指针,右移左指针会把一个区间的集合删掉一个数,如果是唯一最大值则不断右移指针直至重新加入相同的数。考虑找到相同 x 的,包含此区间的下一次操作即可。二分。

时间复杂度可能是 nlog2n(假设 n,m 同阶)。

n,m106

i 为右端点,考虑最大的左端点 li 满足 sol(li,i)=sol(1,m)

从大到小考虑 x,由于是和全局操作相等,每次只需要考虑操作 1mai=x 的位置。接下来仅考虑 xi=x 的操作。

用并查集把所有操作 1mai=x 的位置 i 提出来(或者你直接求出最后的序列)。把每个操作的左右端点放到这些位置上二分出对应的区间。重新编号序列和操作。

考虑维护 ti 为最大的数使得仅考虑 xi=x 的操作,[ti,i] 的操作区间并能覆盖全局(这些位置)。使用颜色段均摊或者线段树,第 i 个操作把 [li,ri] 推平为 i,操作完第 i 个操作,ti 即为现在序列中的最小值。由于 ti 有单调性,维护一个指针往后扫即可。

设考虑的第 i 个操作在原操作序列中的位置为 idi,则对于 [idk,idk+1) 的操作,有且仅有 k 一个 xi=x 的操作,必然要取到 tk 使得所有最终等于 x 的数符合要求。所以 i[idk,idk+1),limin(li,tk),注意还需要 i[1,id1),li0

由于 t 的单调性,可以直接看为对前缀取 min,在 idk+11 处标记一下,最后做一个后缀 min 即可。

最终答案是 i=1nli。得到题目中的 fi 只需要再做一遍后缀和即可。

时间复杂度 O(nlogn),瓶颈在于排序、二分、颜色段均摊。可以通过神秘数据结构做到 O(nloglogn),但不很有趣。

代码(仅供参考):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1e6+10;
int n,m,lp[N],f[N],cnt[N],sf[N],s2[N];
struct oper{
    int l,r,id;
}a[N];
vector<oper>by[N];
int fa[N];
inline int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
struct node{
    int l;
    mutable int r,v;
    node(int L=0,int R=-1,int V=0){ l=L,r=R,v=V; }
    inline friend bool operator<(const node &a,const node &b){
        return a.l<b.l;
    }
};
set<node>tr;
#define sit set<node>::iterator
inline sit split(int p){
    sit it=tr.lower_bound(node(p));
    if(it!=tr.end()&&it->l==p)
        return it;
    --it;
    int mr=it->r,mv=it->v;
    it->r=p-1;
    return tr.insert(node(p,mr,mv)).first;
}
inline void update(int l,int r,int c){
    sit itr=split(r+1),itl=split(l);
    for(sit it=itl;it!=itr;++it){
        cnt[it->v]-=(it->r)-(it->l)+1;
        cnt[c]+=(it->r)-(it->l)+1;
    }
    tr.erase(itl,itr);
    tr.insert(node(l,r,c));
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++)
        a[i].l=read(),a[i].r=read(),a[i].id=i,
        by[read()].push_back(a[i]);
    for(int i=1;i<=m+1;i++)
        sf[i]=2e9;
    for(int i=1;i<=n+1;i++)
        fa[i]=i;
    for(int i=m;i>=1;i--){
        if(!by[i].size()) continue;
        vector<int>p;p.clear();
        for(auto j:by[i])
            for(int k=find(j.l);k<=j.r;k=find(k))
                p.push_back(k),fa[k]=k+1;
        int u=p.size(),ml=0;
        if(!p.size()) continue;
        tr.clear();tr.insert(node(1,u+1,0));
        sort(p.begin(),p.end());
        cnt[0]=u;
        for(int j=1;j<=by[i].size();j++)
            cnt[j]=0;
        sf[by[i][0].id-1]=min(sf[by[i][0].id-1],0);
        for(int t=0;t<by[i].size();t++){
            oper j=by[i][t];
            int pl=lower_bound(p.begin(),p.end(),j.l)-p.begin()+1;
            int pr=upper_bound(p.begin(),p.end(),j.r)-p.begin();
            if(pl<=pr) update(pl,pr,t+1);
            while(cnt[ml]==0) ml++;
            if(t!=by[i].size()-1) sf[by[i][t+1].id-1]=min(sf[by[i][t+1].id-1],ml?by[i][ml-1].id:0);
            else sf[m]=min(sf[m],ml?by[i][ml-1].id:0);
        }
    }
    for(int i=m;i>=1;i--)
        sf[i]=min(sf[i],sf[i+1]);
    for(int i=1;i<=m;i++)
        s2[sf[i]]++;
    for(int i=m;i>=1;i--)
        s2[i]+=s2[i+1];
    unsigned a1=0,a2=0,a3=0;
    for(unsigned i=1;i<=m;i++)
        a1+=s2[i],a2^=s2[i]*i,a3+=s2[i]*i;
    write(a1),putc(' '),write(a2),putc(' '),write(a3);
    flush();
}

再见 qwq~

posted @   ffffyc  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
点击右上角即可分享
微信分享提示