1. 程式人生 > >洛谷——P2434 [SDOI2005]區間

洛谷——P2434 [SDOI2005]區間

整數 getchar() color 合並 ask 說明 排列 esp change

P2434 [SDOI2005]區間

題目描述

現給定n個閉區間[ai, bi],1<=i<=n。這些區間的並可以表示為一些不相交的閉區間的並。你的任務就是在這些表示方式中找出包含最少區間的方案。你的輸出應該按照區間的升序排列。這裏如果說兩個區間[a, b]和[c, d]是按照升序排列的,那麽我們有a<=b<c<=d。

請寫一個程序:

讀入這些區間;

計算滿足給定條件的不相交閉區間;

把這些區間按照升序輸出。

輸入輸出格式

輸入格式:

第一行包含一個整數n,3<=n<=50000,為區間的數目。以下n行為對區間的描述,第i行為對第i個區間的描述,為兩個整數1<=ai<bi<=1000000,表示一個區間[ai, bi]。

輸出格式:

輸出計算出來的不相交的區間。每一行都是對一個區間的描述,包括兩個用空格分開的整數,為區間的上下界。你應該把區間按照升序排序。

輸入輸出樣例

輸入樣例#1:
5
5 6
1 4
10 10
6 9
8 10
輸出樣例#1:
1 4
5 10

思路:先使用很基礎的操作,也就是線段覆蓋的方式,將每一條線段插入線段樹。最後求區間的做法是這樣子的:

設兩個變量a,b。一開始都讓他們等於1.開個while(b<=n);每次查詢區間【a,b】是否被完全覆蓋。是的話,b++;不是的話判斷a是否等於b,如果等於。a++,b++;如果a不等於b那麽輸出a b;然後a=b。

最後跳出循環時也要判斷a是否等於b,等於的話程序結束。不等於的話輸出a b,其實也就是a n。程序結束

註意一個地方!

我們在進行修改的時候修改的是一段區間,但是我們線段樹中的這段區間與我們所說的區間有一點不一樣,例如樣例中的1~4,5~6,我們如果用線段樹進行修改的話我們修改出來的會是

1 1 1 1 1 1 也就是說我們將1~6整個區間都給修改了,但是我們所說的修改是要將其變成1 1 1 0 1 1 也就是說4這個點不進行修改。那麽我們在修改的時候就講尾節點-1以後再進行區間修改就好了。

代碼:

#include<cstdio>
#include<cstdlib>
#include
<cstring> #include<iostream> #include<algorithm> #define N 410000 using namespace std; int n,m,x,y,xx[N],yy[N],ans,sum; int read() { int x=0,f=1; char ch=getchar(); while(ch<0||ch>9){if(ch==-)f=-1; ch=getchar();} while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();} return x*f; } struct Tree { int l,r,w,f; }tree[N]; void build(int k,int l,int r) { tree[k].l=l,tree[k].r=r; if(tree[k].l==tree[k].r) { tree[k].w=0; return ; } int m=(l+r)>>1; build(k<<1,l,m); build(k<<1|1,m+1,r); } int down(int k) { tree[k<<1].f=tree[k].f; tree[k<<1|1].f=tree[k].f; tree[k<<1].w=tree[k<<1].r-tree[k<<1].l+1; tree[k<<1|1].w=tree[k<<1|1].r-tree[k<<1|1].l+1; tree[k].f=0; } void change_interval(int k) { if(tree[k].l>=x&&tree[k].r<=y) { tree[k].w=tree[k].r-tree[k].l+1; tree[k].f=1; return; } if(tree[k].f) down(k); int m=(tree[k].l+tree[k].r)>>1; if(x<=m) change_interval(k<<1); if(y>m) change_interval(k<<1|1); tree[k].w=tree[k<<1].w+tree[k<<1|1].w; } void ask_interval(int k) { if(tree[k].l>=x&&tree[k].r<=y) { ans+=tree[k].w; return; } if(tree[k].f) down(k); int m=(tree[k].l+tree[k].r)>>1; if(x<=m) ask_interval(k<<1); if(y>m) ask_interval(k<<1|1); } int main() { n=read(); for(int i=1;i<=n;i++) xx[i]=read(),yy[i]=read(),m=max(m,max(xx[i],yy[i])); build(1,1,m); for(int i=1;i<=n;i++) x=xx[i],y=yy[i]-1,change_interval(1); x=1,y=1; while(y<=m) { ans=0;ask_interval(1); if(ans==y-x+1) y++; else { if(x==y) x++,y++; else { printf("%d %d\n",x,y); x=y; } } } if(x!=y) printf("%d %d",x,m); return 0; }

原來是打算用來練線段樹的一道題,結果做了一下午,而且這道題暴力,貪心,前綴和都可以做!!!!

唉,發一個貪心的吧、、、按左端點排序後,記錄當前區間[L,R],如果新的區間的l<=R,說明新的區間可以合並,更新R,如果新的區間l>R,說明新的區間不能跟當前區間合並,又因為已經按左端點拍了序,所以輸出當前區間。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 410000
using namespace std;
int n,m,x,y,ans,sum;
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1; ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}
struct A
{
    int l,r;
}a[N];
int cmp(A a,A b)
{
    return a.l<b.l;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
     a[i].l=read(),a[i].r=read();
    sort(a+1,a+1+n,cmp);
    int l=a[1].l,r=a[1].r;
    for(int i=2;i<=n;i++)
    {
        if(a[i].l<=r) r=max(r,a[i].r);
        else 
        {
            printf("%d %d\n",l,r);
            l=a[i].l;r=a[i].r;
        }
    }
    printf("%d %d",l,r);
    return 0;
}

洛谷——P2434 [SDOI2005]區間