YACS 2022年9月月赛 乙组 T1 区间交集(二) 题解

题目链接

错误的单调队列做法

刚看到这题我觉得是一个类单调队列做法。

先把输入的区间从左到右排序,然后把和第一个区间有交集的区间放到 deque 里。

到第二个时就把前面不合法的删掉,后面如果有新的合法的,就把他放进去。

这样,每一轮结束时 deque 的长度 1 (自己不能和自己相交)就是这个和区间相交的区间个数了。

最后还要除以 2 ,因为 ab 相交,那么 ba 也会相交。

结果我一交,全错了,但是样例是全对的。。。

自信的代码:

复制代码
#include <deque>
#include <iostream>
#include <algorithm>
using namespace std;
long long ans,tail = 1;
struct Node
{
    int l,r;
}a[300010];
bool cmp(Node n1,Node n2)
{
    return n1.l < n2.l || n1.l == n2.l && n1.r < n2.r;
}
int n;
deque<Node> d;
int main()
{
    cin >> n;
    for(int i = 1;i <= n;i ++) cin >> a[i].l >> a[i].r;
    sort(a + 1,a + n + 1,cmp);
    for(int i = 1;i <= n;i ++)
    {
        while(!d.empty() && d[0].r < a[i].l) d.pop_front();
        while(tail != n + 1 && a[tail].l <= a[i].r)
        {
            d.push_back(Node{a[tail].l,a[tail].r});
            tail ++;
        }
        ans += d.size() - 1;
    }
    cout << ans / 2 << endl;
    return 0;
}
复制代码

找了半天一个错误样例都找不到,也分析不出来。

后来我用了 rand 函数生成了长度为 5 的序列,发现果然有问题啊!

这个做法的问题就在于他不会将后面不合法的删掉,除非把前面的全删完。

随便出一个不合法的数据:

3

1 5

2 2

3 3

[1,5] 的回合时,他会把 [2,2][3,3] 都放进来,

[2,2] 的回合时,他只会检查前面的 [1,5] 是否符合要求而不会检查后面的 [3,3] 能不能和自己相交导致多算。

所以,这种做法是错误的。

正解,二分

我们来想一想在排好序的区间集合中,和 a 相交的区间集合 b。(假设 a 是左边的区间)

b 在排好序的区间集合中是不是连续的一段呢?

显然是,我们来看下和 a 相交的区间满足什么要求:

假设这个区间是 c ,那么 a 的右端点一定要大于等于 c 的左端点。

也就是说所有左端点小于等于 a 的右端点并且大于等于 a 的左端点的都可以。(为了不算重复,我们假设 a 是左边的区间)

画个图,就是这样:

 

因为我们是把区间按照左端点排序的,所以我们可知:符合条件的区间一定是连续的一段。

那么,我们就可以二分他的右端点了。

那么,左端点是谁呢?

这个也好找,就是 a 后面的第一个区间。

每次二分完右端点后,我们只需要拿他减掉 a 的序号就可以得到和 a 相交的区间个数了( a 是左边的区间),这样也不会算重。

最后需要注意下数据范围

十年心血一场空,不开 longlong 见祖宗。

证明结束,该算法时间复杂度 O(nlogn)

展示一下代码:

复制代码
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
int n,ans;
struct Sec
{
    int l,r;
}a[300010];
bool cmp(Sec s1,Sec s2)
{
    return s1.l < s2.l || s1.l == s2.l && s1.r < s2.r;
}
int check(int x)
{
    int l = 1,r = n,mid;
    int ret = n + 1;
    while(l <= r)
    {
        mid = l + r >> 1;
        if(a[mid].l > x)
        {
            ret = mid;
            r = mid - 1;
        }
        else l = mid + 1;
    }
    return ret;
}
signed main()
{
    cin >> n;
    for(int i = 1;i <= n;i ++) scanf("%d%d",&a[i].l,&a[i].r);
    sort(a + 1,a + n + 1,cmp);
    for(int i = 1;i <= n;i ++) ans += check(a[i].r) - i - 1;
    cout << ans << endl;
    return 0;
}
复制代码

 

posted @   Xy_top  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示