Codeforces Round #716 (Div. 2) D. Cut and Stick 主席树 + 思维
傳送門
文章目錄
- 題意:
- 思路:
題意:
給你個長為nnn的數組aaa,定義好的區間為這個區間中每個數出現的次數≤?n2?\le \left \lceil \frac{n}{2} \right \rceil≤?2n??,定義劃分為將這個區間的若干個子序列拿出來構成若干區間。有qqq個詢問,每次詢問一個區間,問將這個區間最少劃分為多少個區間才能使每個區間都是好區間。
思路:
要注意劃分出來的區間是原區間的子序列,且劃分后的區間也要是好區間,讀錯題做了一個多小時,自閉啦。
注意到子序列,所以我們可以將其分開考慮。假設當前區間出現次數最多的是xxx次,長度為lenlenlen,限制是limitlimitlimit,下面分情況討論:
(1)(1)(1) 當x≤limitx\le limitx≤limit的時候顯然分成一個區間即可。
(2)(2)(2) 當x>limitx>limitx>limit的時候,我們需要將區間劃分,先算出好的數為len?xlen-xlen?x,我們盡可能多的讓xxx與這些好的數放在一起,由于其只有len?xlen-xlen?x個,所以我們最多可以跟他一起放len?x+1len-x+1len?x+1個,剩下來了x?(len?x+1)x-(len-x+1)x?(len?x+1)個,這些我們發現只能讓他們單獨為一個區間,所以最后需要的區間數量即為1+x?(len?x+1)=2?x?len1+x-(len-x+1)=2*x-len1+x?(len?x+1)=2?x?len,問題就轉化成了求xxx,這個方法很多,直接用莫隊就可以秒了,這里介紹一個主席樹的O(nlogn)O(nlogn)O(nlogn)的做法。
先按照權值建主席樹,每次將[l,r][l,r][l,r]的區間拎出來,信息存的是值的個數,我們直接在主席樹上二分,當cntl>limitcnt_l>limitcntl?>limit的時候遞歸左邊,cntr>limitcnt_r>limitcntr?>limit的時候遞歸右邊,否則返回111即可。最后遞歸到l==rl==rl==r的時候就找到了出現次數最多的次數xxx,直接算答案即可。這樣是正確的原因是他需要x>limitx>limitx>limit,而limit=?n2?limit = \left \lceil \frac{n}{2} \right \rceillimit=?2n??,所以他所在的一邊一定cnt>limitcnt>limitcnt>limit,所以正確性得證。
題解貌似有個O(logn)O(logn)O(logn)的做法,上完課再看吧…
// Problem: D. Cut and Stick // Contest: Codeforces - Codeforces Round #716 (Div. 2) // URL: https://codeforces.com/contest/1514/problem/D // Memory Limit: 512 MB // Time Limit: 3000 ms // // Powered by CP Editor (https://cpeditor.org)//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native") //#pragma GCC optimize(2) #include<cstdio> #include<iostream> #include<string> #include<cstring> #include<map> #include<cmath> #include<cctype> #include<vector> #include<set> #include<queue> #include<algorithm> #include<sstream> #include<ctime> #include<cstdlib> #define X first #define Y second #define L (u<<1) #define R (u<<1|1) #define pb push_back #define mk make_pair #define Mid (tr[u].l+tr[u].r>>1) #define Len(u) (tr[u].r-tr[u].l+1) #define random(a,b) ((a)+rand()%((b)-(a)+1)) #define db puts("---") using namespace std;//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); } //void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); } //void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> PII;const int N=300010,mod=1e9+7,INF=0x3f3f3f3f; const double eps=1e-6;int n,m; int ans[N],a[N]; int root[N],tot; struct Node {int l,r,id; }q[N]; struct Tree {int l,r;int cnt; }tr[N*40];void insert(int p,int &q,int l,int r,int pos) {q=++tot; tr[q]=tr[p];tr[q].cnt++;if(l==r) return;int mid=l+r>>1;if(pos<=mid) insert(tr[p].l,tr[q].l,l,mid,pos);else insert(tr[p].r,tr[q].r,mid+1,r,pos); }int query(int p,int q,int l,int r,int k) {int limit=k/2+(k%2==1);if(l==r){int cnt=tr[q].cnt-tr[p].cnt;if(cnt<=limit) return 1;return 2*cnt-k;}int mid=l+r>>1;int cntl=tr[tr[q].l].cnt-tr[tr[p].l].cnt;int cntr=tr[tr[q].r].cnt-tr[tr[p].r].cnt;if(cntl<=limit&&cntr<=limit) return 1;if(cntl<=limit&&cntr>limit) return query(tr[p].r,tr[q].r,mid+1,r,k);if(cntr<=limit&&cntl>limit) return query(tr[p].l,tr[q].l,l,mid,k);return max(query(tr[p].l,tr[q].l,l,mid,k),query(tr[p].r,tr[q].r,mid+1,r,k)); }int main() { // ios::sync_with_stdio(false); // cin.tie(0);scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&a[i]),insert(root[i-1],root[i],1,300000,a[i]);for(int i=1;i<=m;i++){int l,r; scanf("%d%d",&l,&r);printf("%d\n",query(root[l-1],root[r],1,300000,r-l+1));}return 0; } /**/總結
以上是生活随笔為你收集整理的Codeforces Round #716 (Div. 2) D. Cut and Stick 主席树 + 思维的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抖音如何更换自己的头像
- 下一篇: Codeforces Round #71