CodeForces - 1354D Multiset(线段树/二分)
生活随笔
收集整理的這篇文章主要介紹了
CodeForces - 1354D Multiset(线段树/二分)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目鏈接:點擊查看
題目大意:規定在一個 multiset 中初始時有 n 個元素,隨后有 m 次操作,每次操作給出一個 num:
最后要求輸出 multiset 中的任意一個元素,如果最后集合為空,則輸出 0
題目分析:兩種做法,一種簡單無腦,另一種很巧妙
先說簡單無腦的,就是直接模擬,一開始想用 multiset 直接模擬,但是想了一下發現 multiset 無法快速找到第 k 大的數,也就只能放棄了,再一看數據范圍,每個 num 都小于等于 n ,所以直接開線段樹做就行了,維護 [ 1 , n ] 共 n 個位置,插入時相應的位置計數 +1 ,刪除時二分找到相應的位置然后計數 -1 ,最后如果不為空的話隨便輸出一個數就行了,簡單無腦,但是有個需要注意的地方是,這個題目限制了內存大小,我習慣寫線段樹每個節點都包含著 l 和 r ,交了一發直接 MLE 了,于是就只能稍作修改優化一下內存才能勉強 AC
還有一種比較巧妙的方法,因為題目要求輸出集合中的任意一個元素,所以我們可以直接選擇輸出最小的那個元素,至于對最小元素的確定,可以使用二分來定位,將模擬問題轉換為判定問題,寫一個 check( int mid ) 函數,檢查一下 mid 是否可以作為最小的元素,檢查的方法也很簡單,具體實現可以看代碼
代碼:
線段樹
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;int a[N],sum[N<<2];void build(int k,int l,int r) {if(l==r){sum[k]=a[l];return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);sum[k]=sum[k<<1]+sum[k<<1|1]; }void update1(int k,int pos,int l,int r) {if(l==r){sum[k]++;return;}int mid=l+r>>1;if(mid>=pos)update1(k<<1,pos,l,mid);elseupdate1(k<<1|1,pos,mid+1,r);sum[k]=sum[k<<1]+sum[k<<1|1]; }void update2(int k,int rk,int l,int r) {if(l==r){sum[k]--;return;}int mid=l+r>>1;if(rk<=sum[k<<1])update2(k<<1,rk,l,mid);elseupdate2(k<<1|1,rk-sum[k<<1],mid+1,r);sum[k]=sum[k<<1]+sum[k<<1|1]; }void get_ans(int k,int l,int r) {if(l==r){printf("%d\n",l);exit(0);}int mid=l+r>>1;if(sum[k<<1])get_ans(k<<1,l,mid);elseget_ans(k<<1|1,mid+1,r); }int main() { #ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); #endif // ios::sync_with_stdio(false);int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int num;scanf("%d",&num);a[num]++;}build(1,1,n);while(m--){int num;scanf("%d",&num);if(num>0)update1(1,num,1,n);elseupdate2(1,-num,1,n);}if(sum[1]==0)puts("0");elseget_ans(1,1,n);return 0; }二分:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;int a[N],b[N],n,m;bool check(int mid) {int pos=0;//數 mid 最后一次出現時的序號 for(int i=1;i<=n;i++)if(a[i]<=mid)pos++;for(int i=1;i<=m;i++){if(b[i]>0&&b[i]<=mid)//在pos前插入一個數,那么序號加一pos++;if(b[i]<0&&abs(b[i])<=pos)//刪除序號前的一個數,則序號減一pos--;}return pos>0;//序號大于 0 時才算合法 }int main() { #ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); #endif // ios::sync_with_stdio(false);scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",a+i);for(int i=1;i<=m;i++)scanf("%d",b+i);int l=1,r=inf,ans=0;while(l<=r){int mid=l+r>>1;if(check(mid)){ans=mid;r=mid-1;}elsel=mid+1;}printf("%d\n",ans);return 0; }?
總結
以上是生活随笔為你收集整理的CodeForces - 1354D Multiset(线段树/二分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU - 5176 The Exper
- 下一篇: CodeForces - 1354E G