日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CodeForces - 1354D Multiset(线段树/二分)

發布時間:2024/4/11 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CodeForces - 1354D Multiset(线段树/二分) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:規定在一個 multiset 中初始時有 n 個元素,隨后有 m 次操作,每次操作給出一個 num:

  • num > 0 時:向 multiset 中添加 num
  • num < 0 時:刪除 multiset 中第 (-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(线段树/二分)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。