Codeforces 786C Till I Collapse[主席樹][二分]
阿新 • • 發佈:2019-02-08
題意:給你n個數,問最少能把這n個數分成連續的幾段,且每段中不同的個數小於等於k個,輸出k從1到n的答案。
分析:我們知道i~(i,,,n)的不同數的個數肯定是遞增的,所以對於每個i,我們可以通過二分得出一個最大的j使[i,j]中不同的數個數<=k。那麼問題的關鍵在於,如何知道[i,j]這樣一個區間中,不同的數的個數。
我們可以利用主席樹,以root[i]為頂點的線段樹存的是,[i,n]中不同的數個數,對於每顆樹,將每個第一次出現的數的位置 置為1,其他重複出現的位置 置為0。
那麼,對於每個k,剛開始我們位於root[1],二分找出值為k+1的位置r,ans[k]++,並將當前位置置成root[r],這樣,我們面對的就是[r,n]這段數,直到r>n停止。
程式碼中:ed[i]表示最先出現i的位置,Next[i]表示在i位置的數下一次出現在哪個位置。
以下是程式碼:
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<map> #include<set> #include<stack> #include<cstring> #include<string> #include<vector> //#include<unordered_set> //#include<unordered_map> #include<cmath> using namespace std; #define ull unsigned long long #define ll long long #define lson l,mid,id<<1 #define rson mid+1,r,id<<1|1 typedef pair<int, int>pii; typedef pair<ll, ll>pll; typedef pair<double, double>pdd; const double eps = 1e-6; const int MAXN = 100005; const int MAXM = 5005; const ll LINF = 0x3f3f3f3f3f3f3f3f; const int INF = 0x3f3f3f3f; const int MOD = 1000000007; const double FINF = 1e18; struct { int lc, rc, sum; }st[MAXN * 100]; int n, ed[MAXN], Next[MAXN]; int rt[MAXN], a[MAXN],cnt, ans[MAXN]; void update(int l, int r, int &x, int y, int pos , int val) { st[++cnt] = st[y], st[cnt].sum+=val, x = cnt; if (l == r)return; int mid = (l + r) / 2; if (pos <= mid)update(l, mid, st[x].lc, st[y].lc, pos, val); else update(mid + 1, r, st[x].rc, st[y].rc, pos, val); } int find(int l, int r, int x, int pos) { if (l == r)return l; int mid = (l + r) >> 1; if (st[st[x].lc].sum >= pos)return find(l, mid, st[x].lc, pos); else return find(mid + 1, r, st[x].rc, pos - st[st[x].lc].sum); } int main() { memset(ans, 0, sizeof(ans)); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); ed[i] = n + 1; } for (int i = n; i >= 1; --i) { Next[i] = ed[a[i]]; ed[a[i]] = i; } for (int i = 1; i <= n; ++i)update(1, n + 1, rt[1], rt[1], ed[i], 1); for (int i = 2; i <= n; ++i) { update(1, n + 1, rt[i], rt[i - 1], i - 1, -1); update(1, n + 1, rt[i], rt[i], Next[i - 1], 1); } int l, r, L, R, mid, tmp; for (int i = 1; i <= n; ++i) { int now = 1; while (now <= n) { now = find(1, n + 1, rt[now], i + 1); ans[i]++; } } for (int i = 1; i <= n; ++i)cout << ans[i] << " "; } /* 5 1 3 4 3 3 */