HNOI2009 夢幻布丁 線段樹合併

2021-09-11 15:11:17 字數 1673 閱讀 9141

一開始是在splay專題下找的這道題,但是一想,好像更貼近線段樹的區間連續問題,然後就想著先去用線段樹去寫這道題,聽聞會出現顏色會超出1e7的範疇,所以,我就不考慮用陣列去開了,直接寫了map<>的來解決這個問題。

利用線段樹合併,對於已有的每個顏色來看,我們可以去知道最開始的情況,可以直接求得,然後就是考慮每次的更新,將乙個顏色變成另乙個顏色的時候不要忘記去初始化這個顏色清空,不然,下次要是還改變這個顏色,豈不是在無中生有?(就是這個問題,一開始沒注意到,wa了好幾次……qaq)

#include #include #include #include #include #include #include #include #include #include #include #include #define lowbit(x) ( x&(-x) )

#define pi 3.141592653589793

#define e 2.718281828459045

#define inf 0x3f3f3f3f

#define half (l + r)>>1

#define lsn rt<<1

#define rsn rt<<1|1

#define lson lsn, l, mid

#define rson rsn, mid+1, r

#define ql lson, ql, qr

#define qr rson, ql, qr

#define myself rt, l, r

using namespace std;

typedef unsigned long long ull;

typedef long long ll;

const int maxn = 1e6 + 7;

int n, q, tot, ans;

struct node

}t[maxn<<1];

void pushup(int rt)

void update(int &rt, int l, int r, int k)

int mid = half;

if(k <= mid) update(t[rt].lc, l, mid, k);

else update(t[rt].rc, mid + 1, r, k);

pushup(rt);

}void merge(int &p1, int &p2, int l, int r)

maproot; //為了避免root的時候的顏色數量不確定,故利用map來寫(離散作用)

int main()

int op, now, to;

while(q--)

ans -= t[root[now]].val + t[root[to]].val;

merge(root[to], root[now], 1, n);

ans += t[root[to]].val;

}else printf("%d\n", ans);

}return 0;}/*

4 31 2 2 1

21 2 12*/

/*9 9

1 2 5 3 1 1 2 3 2

21 2 1

21 1 3

21 1 3

21 5 3

2ans:86

331*/

HNOI2009 夢幻布丁

題意 n個布丁擺成一行,進行m次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色.例如顏色分別為1,2,2,1的四個布丁一共有3段顏色.對每個顏色的位置維護鍊錶。合併兩個顏色,連線鍊錶,統計貢獻。統計貢獻的複雜度是與鍊錶長度有關的。如果遍歷長度短的鍊錶那麼複雜度自然更小...

HNOI2009 夢幻布丁

n個布丁擺成一行,進行m次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色.例如顏色分別為1,2,2,1的四個布丁一共有3段顏色.輸入格式 第一行給出n,m表示布丁的個數和好友的操作次數.第二行n個數a1,a2.an表示第i個布丁的顏色從第三行起有m行,對於每個操作,若...

HNOI2009 夢幻布丁

題目鏈結 把每一種數字想象成乙個佇列。如果暴力合併,時間複雜度必然很高,考慮啟發式合併 每次把數字少的佇列合併到數字多的佇列上去。每次合併,若數字少的佇列數字個數為 s 則合併之後產生新佇列的大小必定不小於 2 s 時間複雜度 o n log n 這樣合併產生了乙個問題 即原來要求將 a 全部變為 ...