潜龙未见静水流,沉默深藏待时秋。一朝破空声势振,惊世骇俗展雄猷。
随笔 - 82, 文章 - 0, 评论 - 3, 阅读 - 2153

CF2018E2 Complex Segments (Hard Version) 题解

目录

题目描述

T 组数据,给定 n 条线段 [li,ri] ,称一个线段集合是复杂的,当且仅当:

  • 它可以被划分成若干个大小相等的线段组。
  • 两条线段相交当且仅当它们在同一组。

求用这 n 条线段构成的复杂线段集合大小的最大值。

数据范围

  • 1n,n3105
  • 1liri2n

分析

f(m) 为线段组大小为 m 时的最大组数,目标是求 mf(m) 的最大值。

显然 f(m)nm 且单调递减,于是本质不同的 f(m) 只有 O(n) 种。接下来是经典的分治做法在 O(n+ncalc) 的时间复杂度内求所有 f(m)

void solve(int l,int r,int L,int R)
{
    if(l>r) return ;
    if(L==R)
    {
        for(int i=l;i<=r;i++) f[i]=l;
        return ;
    }
    int mid=(l+r)>>1,val=calc(mid);
    f[mid]=val;
    solve(l,mid-1,L,val);
    solve(mid+1,r,val,R);
}

对于本题,将所有线段按照右端点从小到大扫描,维护每个位置被几条线段覆盖,线段树维护区间加区间 max ,可以做到 O(nlogn) 计算单个 f(m) 的值。

至此 O(nnlogn) 已经可以通过 E1 了,接下来是人类智慧。

先用基数排序操作一下,使得所有端点互不相同。

维护被覆盖次数为后缀 max 的所有下标 q1,,qk ,那么 qi 被覆盖次数为 k+1i

每次插入一条线段 [l,r] ,我们将 r 加入下标集合,并删除 l 之前的最小下标。

并查集中 fi 指向 i (含)前面最后一个在 q1,,qk 中的点即可。

时间复杂度 O(nnα(n))

#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=6e5+5;
int n,t,res;
int c[maxn],f[maxn];
pii p[maxn];
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
int calc(int m)
{
    int cnt=0;
    for(int i=1,x=0,cur=0,lim=0,lst=0;i<=n;i++)
    {
        int l=p[i].fi,r=p[i].se;
        if(l<=lim) continue;
        for(int j=lst+1;j<r;j++) f[j]=lst;
        cur++,lst=f[r]=r,x=find(l);
        if(x>lim) cur--,f[x]=x-1;
        if(cur==m) cnt++,cur=0,lim=r;
    }
    res=max(res,cnt*m);
    return cnt;
}
void solve(int l,int r,int L,int R)
{
    if(l>r||L==R) return ;
    int mid=(l+r)>>1,val=calc(mid);
    solve(l,mid-1,L,val),solve(mid+1,r,val,R);
}
int main()
{
    for(scanf("%d",&t);t--;)
    {
        scanf("%d",&n),memset(c,0,8*n),res=0;
        for(int i=1;i<=n;i++) scanf("%d",&p[i].fi),c[p[i].fi]++;
        for(int i=1;i<=n;i++) scanf("%d",&p[i].se),c[p[i].se]++;
        for(int i=1;i<=2*n;i++) c[i]+=c[i-1];
        for(int i=1;i<=n;i++) p[i].se=c[p[i].se]--;
        for(int i=1;i<=n;i++) p[i].fi=c[p[i].fi]--;
        sort(p+1,p+n+1,[&](pii x,pii y){return x.se<y.se;});
        solve(1,n,n+1,0);
        printf("%d\n",res);
    }
    return 0;
}

总结

  • 3400 的 E2 确实不好想,但 3300 的 E1 绝对是虚高,场上有一些细节没处理好呜呜呜
  • f(i)ni 且单调降,十有八九是自然根号,当然也可以用根号分治加二分做到 O(nnlogn) ,具体参见 CF1039D You Are Given a Tree 的题解区。

posted on   peiwenjun  阅读(54)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示