poj 3225 Help with Intervals 線段樹 集合的交併差補運算

這道題感覺比之前的麻煩了很多,而且比較綜合

首先,如何簡單處理開區間和閉區間的問題?我是看了notonlysuccess大神的方法才知道的

將所有的數字乘以2,奇數表示開區間,偶數表示閉區間,如原來的[2,4]在線段樹中則是(4,8),原來的(1,3)在線段樹中是(3,5),要注意的是,如果是左開區間,則左端點的表示是該數乘以2+1,若爲右開區間,則是乘以2減1,(1,3)對應(3,5)就是這麼個例子

然後,如何處理集合的運算呢?依然是大神的方法,在這裏我直接copy過來了,有興趣的話可以去直接看看大神的:
U:把區間[l,r]覆蓋成1
I:把[-∞,l)(r,∞]覆蓋成0
D:把區間[l,r]覆蓋成0
C:把[-∞,l)(r,∞]覆蓋成0 , 且[l,r]區間0/1互換
S:[l,r]區間0/1互換

(1表示該區間在結果中,0表示不在,這些值存於sum中,如果某節點的兩個子節點的值不同,則記爲-1)

有了這個想法,那麼就可以建樹了(注意所有數都被乘過2了,所以MAX必須是n的兩倍大)

這種區間更新區間查詢的題難點還是在update上,延遲更新的問題(之前這些沒處理好,TLE兩次,WA了兩次……)

因爲上述5中操作中有直接覆蓋的,也有0/1反轉的,所以一個只用一個sum是不方便的,如果是直接覆蓋,那麼還是按照常規的update,找到相應的區間,把sum值改了就好,但是如果是0/1反轉,則利用另一個標記c0,c0[rt] = 1表示這個子區間需要反轉一下

每次向下更新或者查詢時,需要push_down一下,即把父節點的信息更新到子節點,如果父節點的sum等於0或者1,那麼子節點的值直接就等於父節點,並且子節點的c0標記直接清0,因爲父節點的值直接說明了直接點的狀態必須是那樣;如果父節點是-1,那麼得看父節點的c0標記,如果爲0,那麼直接不用管了,如果爲1,則,需要對子節點進行哦按段,如果子節點的值不是-1,那麼直接反轉,如果是-1,那麼將子節點的c0標記反轉……做完之後,把父節點的c0清0,因爲他的反轉操作已經執行了(之前因爲這個沒有清零,出了奇怪的錯誤)………………這樣之後,就能正確更新了,而且,能夠確保,所有sum值不是-1的節點,他的c0標記一定是0

下面是查詢的問題,查詢整個題只要查一次就好了,查的是整個區間,0到max,直到sum值爲1時,這個區間纔是答案中存在的,但是不能急着輸出,因爲,可能有這種情況,線段樹(3,4)的值是1,即(1,2]在答案中,同時(5,6)也是1,即(2,3]也在答案中,這時應該輸出的是(1,3]而不是(1,2] (2,3] 所以,我把中間得到的結果存在ans數組中,然後考慮兩個區間要不要合併………………記住查詢的時候也是得push_down的,因爲那些東西都延遲更新了

以及,查到sum=0可以直接返回

另外在update的時候有個地方可以優化,就是當前區間的sum值和你要去覆蓋它的值是相等的時候,就沒必要再遞歸下去了,直接返回即可

以下就是本題代碼:

#include <cstdio>
#include <cstring>
#include <cstdlib>
const int MAX = 150000;
int sum[MAX<<2];
int c0[MAX<<2];
void push_up(int rt)
{
    sum[rt] = (sum[rt<<1]==sum[rt<<1|1])?sum[rt<<1]:-1;
}
void push_down(int rt)
{
    if(sum[rt]!=-1)
    {
        if(c0[rt]) sum[rt] ^= 1;
        c0[rt] = 0;
        sum[rt<<1|1] = sum[rt<<1] = sum[rt];
        c0[rt<<1|1] = c0[rt<<1] = 0;
        return;
    }
    if(c0[rt])
    {
        if(sum[rt<<1]!=-1) sum[rt<<1] ^= 1;
        else c0[rt<<1] ^= 1;
        if(sum[rt<<1|1]!=-1) sum[rt<<1|1] ^= 1;
        else c0[rt<<1|1] ^= 1;
        c0[rt] = 0;
    }
}
void build(int l,int r,int rt)
{
    if(l==r) return;
    int mid = r+l>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}
void update(int p,int L,int R,int l,int r,int rt)
{
    if(p==sum[rt]) return;
    if(L<=l&&R>=r)
    {
        if(p<2)
        {
            c0[rt] = 0;
            sum[rt] = p;
            return;
        }
        if(sum[rt]!=-1)
        sum[rt] ^= 1;
        else
        c0[rt] ^= 1;
        return;
    }
    push_down(rt);
    int mid = r+l>>1;
    if(L<=mid) update(p,L,R,l,mid,rt<<1);
    if(R>mid) update(p,L,R,mid+1,r,rt<<1|1);
    push_up(rt);
}
int ans[MAX];
int cnt;
void push_ans(int l,int r)
{
    if(cnt==0)
    {
        ans[cnt++] = l;
        ans[cnt++] = r;
        return;
    }
    if(l-ans[cnt-1]==1)
    ans[cnt-1] = r;
    else
    {
        ans[cnt++] = l;
        ans[cnt++] = r;
    }
}
void query(int l,int r,int rt)
{
    if(sum[rt]==1)
    {
        push_ans(l,r);
        return;
    }
    if(sum[rt]==0) return;
    push_down(rt);
    int mid = r+l>>1;
    query(l,mid,rt<<1);
    query(mid+1,r,rt<<1|1);
    push_up(rt);
}
void print()
{
    if(cnt==0)
    printf("empty set\n");
    else
    {
        for(int i=0,j=1; j<cnt; j+=2,i+=2)
        {
            if(i) printf(" ");
            if(ans[i]%2) printf("(%d,",ans[i]>>1);
            else printf("[%d,",ans[i]>>1);
            if(ans[j]%2) printf("%d)",ans[j]+1>>1);
            else printf("%d]",ans[j]>>1);
        }
        puts("");
    }
}
int main()
{
    char c;
    char s[100];
    int x,y;
    cnt = 0;
    memset(sum,0,sizeof(sum));
    memset(c0,0,sizeof(c0));
    build(0,MAX,1);
    while(scanf(" %c",&c)==1)
    {
        scanf("%s",s);
        if(s[0]=='(')
        x = atoi(s+1)*2 +1;
        else x =  atoi(s+1)*2;
        if(s[strlen(s)-1]==')')
        y = atoi(strstr(s,",")+1)*2-1;
        else y = atoi(strstr(s,",")+1)*2;
        switch(c)
        {
            case 'U': update(1,x,y,0,MAX,1); break;
            case 'I': update(0,0,x-1,0,MAX,1); update(0,y+1,MAX,0,MAX,1); break;
            case 'S': update(2,x,y,0,MAX,1); break;
            case 'C': update(0,0,x-1,0,MAX,1); update(0,y+1,MAX,0,MAX,1); update(2,x,y,0,MAX,1); break;
            case 'D': update(0,x,y,0,MAX,1); break;
        }
    }
    query(0,MAX,1);
    print();
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章