SSL2785 2017年10月23日提高组T1 询问 (二分+并查集)

2017年10月23日提高组T1 询问

Description
这里写图片描述

Input
这里写图片描述

Output
这里写图片描述

分析:首先二分答案,转化为前 mid 个询问是否矛盾。 把所有询问按照答案从大到小排序,把答案相同的那些询问放在一起,求出区 间交及区间并,如果区间交为空说明序列中有重复数字,直接无解;否则我们看 看区间交是否全被覆盖,如果全被覆盖显然之前的区间答案错误产生矛盾,如 果没全被覆盖我们就把区间并(由于区间交不为空区间并显然是一段连续区间) 进行覆盖。 正确性在于如果有 l1 ≤ l2 ≤ r2 ≤ l1,那么显然有 ans2 ≥ ans1,而如果答案小 的区间全被覆盖就相当于 ans2 < ans1。这就产生了矛盾。 考虑怎么维护,可以使用线段树进行维护,但是更简单的是利用并查集。 暴力覆盖区间中的位置,当覆盖了 i 之后就把 i 合并到 i + 1 的集合下面。 于是 i 在并查集中的根就是 i 所在的最长连续覆盖区间的右端点 +1,也就是说 后面第一个没被覆盖的位置。 覆盖 [l, r] 时就从 l 开始,每次找到下一个没被覆盖的位置,如果还没超过 r, 就把他覆盖然后合并。 查询 [l, r] 是否完全被覆盖只需比较 l 后面第一个没被覆盖位置是否 > r。 这样的话每次覆盖时间复杂度与实际覆盖的位置数有关,那么总复杂度不超过 O(nα(n))。 于是总时间复杂度 O(log Q(Q log Q + nα(n)))。

代码

#include <cstdio>
#include <algorithm>
#define maxn 2000000
using namespace std;

struct edge
{
    int x,y,w;
};
struct arr
{
    int x,y,w;
};
arr a[maxn];
edge b[maxn];
int n,m,f[maxn];

int so(edge p,edge q)
{
    return p.w>q.w;
}

int find1(int x)
{
    if (f[x]==x) return x;
    return f[x]=find1(f[x]);
}

int find(int a)
{    
    int r, j, k ;
     r = a;
     while(r != f[r])
        r = f[r];
    j = a;
     while(j != r)
    {
        k = f[j];
        f[j] = r;
        j = k;
    }
    return r;
}

int max(int x,int y)
{
    return x>y?x:y;
}

int min(int x,int y)
{
    return x<y?x:y;
}

bool check(int x)
{
    for (int i=1;i<=x;i++)
    {
        b[i].x=a[i].x;
        b[i].y=a[i].y;
        b[i].w=a[i].w;
    }
    for (int i=1;i<=n+1;i++)
        f[i]=i;
    sort(b+1,b+x+1,so);
    int fx=b[1].x,fy=b[1].y;
    int gx=fx,gy=fy;
    b[x+1].w=-1;
    for (int i=2;i<=x+1;i++)
        if (b[i].w==b[i-1].w)
        {
            if (b[i].x>fy||b[i].y<fx) return false;
            fx=max(b[i].x,fx);
            fy=min(b[i].y,fy);
            gx=min(b[i].x,gx);
            gy=max(b[i].y,gy);
        }
        else
        {
            int u=find(fx);
            int v=find(fy+1);
            if (u==v) return false;
            u=find(gx);
            v=find(gy+1);
            while (u!=v)
            {
                f[u]=u+1;
                u=find(u+1);
            }
            fx=b[i].x;
            fy=b[i].y;
            gx=fx;gy=fy;
        }
    return true;
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
    int l=1,r=m;
    while (l<r)
    {
        int mid=(l+r)/2;
        if (check(mid)) l=mid+1;
            else r=mid;
    }
    if (l==m&&check(l)) printf("%d",0);else printf("%d",l);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值