【XSY2707】snow 线段树 并查集

题目描述

  有\(n\)个人和一条长度为\(t\)的线段,每个人还有一个工作范围(是一个区间)。最开始整条线段都是白的。定义每个人的工作长度是这个人的工作范围中白色部分的长度(会随着线段改变而改变)。每一天开始时你要选择一个人满足这个人的工作长度最小(如果有多个就选编号最小的)。把这个人的工作区间染黑。请你输出每天你选了哪个人。

  保证工作范围中左端点和右端点单调递增。

  \(n\leq 300000\)

题解

  先把线段离散化成很多个小区间,那么每个小区间只会被染黑一次(染黑之后不会变白)。

  因此每次选择了一个人后可以暴力把这个人的工作范围内白色的区间染黑,那么覆盖了这个区间的人就会受到影响。

  观察到左右端点都单调递增,所以覆盖一个区间的人就是连续的。

  直接用线段树维护每个人当前的工作长度。

  用并查集维护右边的第一个白色区间。

  时间复杂度:\(O(n\log n)\)

代码

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<utility>
#include<cstring>
using namespace std;
typedef pair<int,int> pii;
int a[2000010];
int c[2000010];
int d[2000010];
int f[2000010];
int l[2000010];
int r[2000010];
int s[2000010];
int rt;
namespace seg
{
    struct node
    {
        int l,r,ls,rs;
        int t;
        pii s;
        node()
        {
            l=r=ls=rs=t=0;
        }
    };
    node a[2000010];
    int cnt;
    void build(int &p,int l,int r)
    {
        p=++cnt;
        a[p].l=l;
        a[p].r=r;
        if(l==r)
        {
            a[p].s=pii(::s[l],l);
            return;
        }
        int mid=(l+r)>>1;
        build(a[p].ls,l,mid);
        build(a[p].rs,mid+1,r);
        a[p].s=min(a[a[p].ls].s,a[a[p].rs].s);
    }
    void add(int p,int v)
    {
        a[p].t+=v;
        a[p].s.first+=v;
    }
    void push(int p)
    {
        if(a[p].t&&a[p].l!=a[p].r)
        {
            add(a[p].ls,a[p].t);
            add(a[p].rs,a[p].t);
            a[p].t=0;
        }
    }
    void add(int p,int l,int r,int v)
    {
        if(l<=a[p].l&&r>=a[p].r)
        {
            add(p,v);
            return;
        }
        push(p);
        int mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            add(a[p].ls,l,r,v);
        if(r>mid)
            add(a[p].rs,l,r,v);
        a[p].s=min(a[a[p].ls].s,a[a[p].rs].s);
    }
    void gao(int p,int x)
    {
        if(a[p].l==a[p].r)
        {
            a[p].s.first=0x7fffffff;
            return;
        }
        push(p);
        int mid=(a[p].l+a[p].r)>>1;
        if(x<=mid)
            gao(a[p].ls,x);
        else
            gao(a[p].rs,x);
        a[p].s=min(a[a[p].ls].s,a[a[p].rs].s);
    }
    int get(int p,int x)
    {
        if(a[p].l==a[p].r)
            return a[p].s.first;
        push(p);
        int mid=(a[p].l+a[p].r)>>1;
        if(x<=mid)
            return get(a[p].ls,x);
        else
            return get(a[p].rs,x);
    }
}
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
int b[1000010];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
#endif
    int n,t;
    scanf("%d%d",&t,&n);
    int m=0;
    int i;
    memset(a,0,sizeof a);
    memset(c,0,sizeof c);
    memset(d,0,sizeof d);
    memset(f,0,sizeof f);
    memset(s,0,sizeof s);
    memset(l,0,sizeof l);
    memset(r,0,sizeof r);
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&l[i],&r[i]);
        r[i]--;
        s[i]=r[i]-l[i]+1;
        a[++m]=l[i];
        a[++m]=r[i]+1;
    }
    sort(a+1,a+m+1);
    m=unique(a+1,a+m+1)-a-1;
    for(i=1;i<=n;i++)
    {
        int x=l[i],y=r[i];
        l[i]=lower_bound(a+1,a+m+1,l[i])-a;
        r[i]=lower_bound(a+1,a+m+1,r[i]+1)-a-1;
        if(a[l[i]]!=x)
            printf("error\n");
        if(a[r[i]+1]!=y+1)
            printf("error\n");
        if(l[i]<=l[i-1])
            printf("error\n");
        if(r[i]<=r[i-1])
            printf("error\n");
    }
    seg::cnt=0;
    rt=0;
    seg::build(rt,1,n);
    for(i=1;i<=m;i++)
    {
        c[i]=a[i+1]-a[i];
        f[i]=i;
    }
    f[m+1]=m+1;
    int j;
    for(i=1;i<=n;i++)
    {
        pii ans=seg::a[rt].s;
//      printf("%d %d\n",seg::get(rt,7396),seg::get(rt,20692));
        printf("%d\n",ans.second);
//      seg::add(rt,ans.second,ans.second,0x7fffffff-ans.first);
        seg::gao(rt,ans.second);
//      for(j=l[ans.second];j<=r[ans.second];j++)
//          if(!b[j])
//          {
//              b[j]=1;
        for(j=find(l[ans.second]);j<=r[ans.second];j=find(j))
        {
            int x=lower_bound(r+1,r+n+1,j)-r;
            int y=upper_bound(l+1,l+n+1,j)-l-1;
            if(x<=y)
            {
//              if(r[x-1]>=j)
//                  printf("error1\n");
//              if(l[y+1]<=j)
//                  printf("error2\n");
                seg::add(rt,x,y,-c[j]);
            }
//          else
//              if(l[7396]<=j&&r[7396]>=j)
    //              seg::add(rt,7396,7396,-c[j]);
            f[j]=j+1;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/ywwyww/p/8513567.html

时间: 2024-10-09 17:16:09

【XSY2707】snow 线段树 并查集的相关文章

【BZOJ 4662】 4662: Snow (线段树+并查集)

4662: Snow Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 136  Solved: 47 Description 2333年的某一天,临冬突降大雪,主干道已经被雪覆盖不能使用.城 主 囧·雪 决定要对主干道进行一次清扫. 临冬城的主干道可以看为一条数轴.囧·雪 一共找来了n个清理工,第 i个清理工的工作范围为[li,ri],也就是说这个清理工会把[li,ri]这一 段主干道清理干净(当然已经被清理过的部分就被忽略了).当然有可能主 干道

UVALive 4730 Kingdom 线段树+并查集

题目链接:点击打开链接 题意见白书P248 思路: 先把读入的y值都扩大2倍变成整数 然后离散化一下 用线段树来维护y轴 区间上每个点的 城市数量和联通块数量, 然后用并查集维护每个联通块及联通块的最大最小y值,还要加并查集的秩来记录每个联通块的点数 然后就是模拟搞.. T^T绝杀失败题..似乎数组开小了一点就过了,== #include<stdio.h> #include<math.h> #include<vector> #include<string.h>

UVA 1455 - Kingdom(线段树+并查集)

UVA 1455 - Kingdom 题目链接 题意:给定一些城市坐标点,连在一起的城市称为一个州,现在用两种操作,road表示把城市a,b建一条路,line表示询问一个y轴上穿过多少个州,和这些州共包含多少个城市 思路:利用并查集维护每个州的上界和下界还有城市个数,然后每次加进一条路的时候,根据两个集合的位置可以处理出区间的州和城市数如何进行加减,然后利用线段树搞就可以了 代码: #include <cstdio> #include <cstring> #include <

(线段树+并查集) Codeforces Round #416 (Div. 2) E Vladik and Entertaining Flags

In his spare time Vladik estimates beauty of the flags. Every flag could be represented as the matrix n?×?m which consists of positive integers. Let's define the beauty of the flag as number of components in its matrix. We call component a set of cel

【BZOJ-3673&amp;3674】可持久化并查集 可持久化线段树 + 并查集

3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status][Discuss] Description n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^4 Input Output Sample Input 5 6

UVALive 4730 线段树+并查集

点击打开链接 题意:在坐标上给n个点,r的操作是将两个点连起来,l的操作是问你y=u的这条线连接的集合块数和这些集合内的点的个数 思路:很麻烦的一道题,在网上看了题意和做法后,开始了一下午的调bug过程,做法很好懂,我开了两个线段树,一个维护点代表的直线的集合个数,另一个则是路过集合内的点的个数,然后集合的判断直接用并查集就行了,这是两个核心,然后就是自己瞎写的了,代码丑的可以而且好像除了本人别人看着可能要骂人了,有兴趣研究的可以留言我来解答,那难的部分其实就是并查集合并时该怎么将这两个要维护的

【Codeforces811E】Vladik and Entertaining Flags [线段树][并查集]

Vladik and Entertaining Flags Time Limit: 20 Sec  Memory Limit: 512 MB Description n * m的矩形,每个格子上有一个数字代表颜色. q次询问,询问[l, r]有几个连通块,若颜色相同并且连通则属于同一个连通块. Input 输入第一行n,m,q. 然后一个n*m的矩形. 之后q行,每行两个整数l,r. Output 输出q行,对于每个询问输出答案. Sample Input 4 5 4 1 1 1 1 1 1 2

【CF687D】Dividing Kingdom II 线段树+并查集

[CF687D]Dividing Kingdom II 题意:给你一张n个点m条边的无向图,边有边权$w_i$.有q个询问,每次给出l r,问你:如果只保留编号在[l,r]中的边,你需要将所有点分成两个集合,使得这个划分的代价最小,问最小代价是什么.一个划分的代价是指,对于所有两端点在同一集合中的边,这些边的边权最大值.如果没有端点在同一集合中的边,则输出-1. $n,q\le 1000,m\le \frac {n(n-1)} 2,w_i\le 10^9$ 题解:先考虑暴力的做法,我们将所有边按

[CSP-S模拟测试]:Dash Speed(线段树+并查集+LCA)

题目描述 比特山是比特镇的飙车圣地.在比特山上一共有$n$个广场,编号依次为$1$到$n$,这些广场之间通过$n−1$条双向车道直接或间接地连接在一起,形成了一棵树的结构. 因为每条车道的修建时间以及建筑材料都不尽相同,所以可以用两个数字$l_i,r_i$量化地表示一条车道的承受区间,只有当汽车以不小于$l_i$且不大于$r_i$的速度经过这条车道时,才不会对路面造成伤害. $Byteasar$最近新买了一辆跑车,他想在比特山飙一次车.$Byteasar$计划选择两个不同的点$S,T$,然后在它