1. 程式人生 > >hdu 1556 塗氣球 線段樹(區間更新~對區間[x,y]更新,求任意節點被更新的次數)

hdu 1556 塗氣球 線段樹(區間更新~對區間[x,y]更新,求任意節點被更新的次數)

Color the ball

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 29526    Accepted Submission(s): 14356


Problem Description N個氣球排成一排,從左到右依次編號為1,2,3....N.每次給定2個整數a b(a <= b),lele便為騎上他的“小飛鴿"牌電動車從氣球a開始到氣球b依次給每個氣球塗一次顏色。但是N次以後lele已經忘記了第I個氣球已經塗過幾次顏色了,你能幫他算出每個氣球被塗過幾次顏色嗎?  

 

Input 每個測試例項第一行為一個整數N,(N <= 100000).接下來的N行,每行包括2個整數a b(1 <= a <= b <= N)。
當N = 0,輸入結束。  

 

Output 每個測試例項輸出一行,包括N個整數,第I個數代表第I個氣球總共被塗色的次數。  

 

Sample Input 3 1 1 2 2 3 3 3 1 1 1 2 1 3 0  

 

Sample Output 1 1 1 3 2 1  
//注意laz[]要和線段樹陣列開一樣大小
#include<iostream>
#include<algorithm>
#include<vector>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
const int  n = 100050;
using namespace std;
void pushdown(int num); int tre[n * 4]; int laz[n*4]; void update(int num, int le, int ri, int x, int y,int z) { if (x <= le && y >= ri) { tre[num] = tre[num] + z;//初值為0,z=1 laz[num] = laz[num] + z; return; } pushdown(num); int mid = (le + ri) / 2; if (x <= mid) update(num * 2, le, mid, x, y, z); if (y > mid) update(num * 2 + 1, mid + 1, ri, x, y, z); } void pushdown(int num) { if (laz[num] != 0) { tre[num * 2] += laz[num]; tre[num * 2 + 1] += laz[num]; laz[num * 2] += laz[num]; laz[num * 2 + 1] += laz[num]; laz[num] = 0; } } int query(int num, int le, int ri, int x) { if (le == ri) { return tre[num]; } pushdown(num); int mid = (le + ri) / 2; if (x <= mid) return query(num * 2, le, mid, x); else return query(num * 2 + 1, mid + 1, ri, x); } int main() { int t; while (cin >> t) { memset(tre, 0, sizeof(tre)); memset(laz, 0, sizeof(laz));//初始化延遲標記 for (int i = 0; i < t; i++) { int x, y; cin >> x >> y; update(1, 1, t, x, y, 1); cout << query(1, 1, n, x) << endl; } } return 0; }

 

用結構體儲存資料

#include <stdio.h>
#include<iostream>
#include <string.h>
using namespace std;
struct node
{
    int left, right, count;
}c[100005 * 3];
int sum[100005];
void build(int le, int ri, int root)
{
    c[root].left = le;
    c[root].right = ri;
    c[root].count = 0;
    if (le == ri)
        return;
    int mid = (le + ri) / 2;
    build(le, mid, root * 2);
    build(mid + 1, ri, root * 2 + 1);
}
void update(int le, int ri, int root)//更新
{
    if (c[root].left == le && c[root].right == ri)//只需要在這個區間+1就行了,節省時間,不用找到每個數
    {
        c[root].count++;
        return;
    }
    int mid = (c[root].left + c[root].right) / 2;
    if (mid<le)//如果更新區間在右子樹上,就只更新右區間
        update(le, ri, root * 2 + 1);
    else if (mid >= ri)//更新左子樹
        update(le, ri, root * 2);
    else//更新區間即在右子樹上,又在左子樹上
    {
        update(le, mid, root * 2);
        update(mid + 1, ri, root * 2 + 1);
    }
}
void tosum(int root)//求和、記錄每個氣球被塗過的次數
{
    for (int i = c[root].left; i <= c[root].right; i++)
        sum[i] += c[root].count;
    if (c[root].left == c[root].right)
        return;
    tosum(root * 2);//通過子節點來更新父節點
    tosum(root * 2 + 1);
}
int main()
{
    int n;
    while (scanf("%d", &n) && n)
    {
        memset(sum, 0, sizeof(sum));
        memset(&c, 0, sizeof(&c));
        build(1, n, 1);
        for (int i = 0; i<n; i++)
        {
            int le, ri;
            scanf("%d %d", &le, &ri);
            update(le, ri, 1);
        }
        tosum(1);
        printf("%d", sum[1]);
        for (int i = 2; i <= n; i++)
            printf(" %d", sum[i]);
        printf("\n");
    }
    return 0;
}