Codeforces Round #401 (Div. 1) C(set+树状数组)
題意: 給出一個(gè)序列,給出一個(gè)k,要求給出一個(gè)劃分方案,使得連續(xù)區(qū)間內(nèi)不同的數(shù)不超過(guò)k個(gè),問(wèn)劃分的最少區(qū)間個(gè)數(shù),輸出時(shí)將k=1~n的答案都輸出
?
比賽的時(shí)候想的有點(diǎn)偏,然后寫(xiě)了個(gè)nlog^2n的做法,T了
賽后發(fā)現(xiàn)有更加巧妙的做法
?
題解:
首先,可以貪心地想
也就是說(shuō)從第一個(gè)數(shù)開(kāi)始,每個(gè)區(qū)間都盡量往后選,直到不能選為止,可以證明這樣是最優(yōu)的
那么如果按照這個(gè)方案,實(shí)際上區(qū)間的選取都是固定的。
所以位置為i的數(shù)有可能是k=1,k=2....k=t的起點(diǎn)
如果當(dāng)前位置是i,我們考慮如何更新答案
首先對(duì)答案有影響的只有在i右邊的不同類別的第一個(gè)數(shù),比如i右邊有1 2 3 1 2,那么有影響的只有前3個(gè)數(shù)
所以考慮動(dòng)態(tài)插入一個(gè)樹(shù)狀數(shù)組
也就是說(shuō)當(dāng)前位置是i,k=t,那么k=t的下一個(gè)區(qū)間的位置就是去找樹(shù)狀數(shù)組內(nèi)的一個(gè)區(qū)間,內(nèi)部有t個(gè)不同的數(shù)(樹(shù)狀數(shù)組也可以查詢這個(gè)問(wèn)題)
然后從1到n枚舉區(qū)間的起點(diǎn),過(guò)程中更新k=t的下一個(gè)位置,并在每個(gè)位置用vector存包含了k為若干值的情況
(可以證明vector最多存nlogn個(gè)點(diǎn),n+n/2+n/3+....+n/n約等于nlogn)
可以使用一個(gè)數(shù)組next存下一個(gè)位置的情況,也可以使用set來(lái)維護(hù)這個(gè)位置信息。
代碼如下
#include <iostream> #include <cstdio> #include <vector> #include <set> #define pb push_back using namespace std; const int maxn = 1e5 + 5; int c[maxn], ans[maxn], a[maxn]; set<int> S[maxn]; vector<int> L[maxn]; int n; void Modify(int x, int s){for(; x <= n; x += x&(-x)) c[x] += s; } int Find(int x){ //實(shí)際上只找x-1個(gè)數(shù)字int p = 0;for(int i = 20; i >= 0; i--){if(p + (1<<i) <= n && c[p + (1<<i)] < x) {x -= c[p + (1<<i)];p += (1<<i);}}return p+1; }void gao(int i){if(!S[i].empty()){Modify(*S[i].begin(), 1);S[i].erase(*S[i].begin());} }int main() {scanf("%d", &n);for(int i = 1; i <= n; i++) scanf("%d", a+i), S[a[i]].insert(i);for(int i = 1; i <= n; i++){L[1].pb(i);gao(i);}for(int i = 1; i <= n; i++){for(auto x : L[i]){int y = Find(x+1);L[y].pb(x);ans[x]++;}Modify(i, -1);gao(a[i]);}for(int i = 1; i <= n; i++) printf("%d ", ans[i]); }?
轉(zhuǎn)載于:https://www.cnblogs.com/Saurus/p/6610316.html
總結(jié)
以上是生活随笔為你收集整理的Codeforces Round #401 (Div. 1) C(set+树状数组)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: JavaScript技巧
- 下一篇: javase学习第10天(形式参数和返回