BZOJ3524:[POI2014]Couriers
阿新 • • 發佈:2018-11-14
struct 表示 getchar() pro hair space .html n) 題解 ,就是操作\([L,R]\)裏插入了多少次值域在\([l,r]\)的數字。如果\([l,mid]\)的次數大於\((R-L+1)/2\)那麽答案就在\([l,mid]\),同理可以去判斷答案是否在\([mid+1,r]\)內,如果都不滿足,那麽就不存在這樣的數值,直接返回\(0\)就行了。
淺談主席樹:https://www.cnblogs.com/AKMer/p/9956734.html
題目傳送門:https://www.lydsy.com/JudgeOnline/problem.php?id=3524
假設數列不是一開始就給你的,而是一次一次修改操作讓你插入數值\(x\),而詢問是問你從第\(L\)次插入到第\(R\)次操作間,有沒有哪個數值插入次數超過\((R-L+1)/2\),把題意這麽一轉化,就很好用主席樹寫了。
我們可以在值域上建立主席樹,每個結點統計值域在\([l,r]\)內的數字有多少個。那麽只需要用第\(r\)個版本的主席樹的\(cnt\)減去第\(l-1\)個版本的主席樹的\(cnt\)
時間復雜度:\(O((n+m)logn)\)
空間復雜度:\(O(nlogn)\)
代碼如下:
#include <cstdio> using namespace std; const int maxn=5e5+5; int n,m; int rt[maxn]; int read() { int x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0'; return x*f; } struct tree_node { int cnt,ls,rs; }; struct Chairman_tree { int tot; tree_node tree[maxn*20]; void updata(int p) { tree[p].cnt=tree[tree[p].ls].cnt+tree[tree[p].rs].cnt; } void ins(int lst,int &now,int l,int r,int pos) { now=++tot;tree[now]=tree[lst];//先把左右兒子信息全部繼承下來 if(l==r) {tree[now].cnt++;return;}//值為l的數字比上一版本的主席樹多了一個 int mid=(l+r)>>1; if(pos<=mid)ins(tree[lst].ls,tree[now].ls,l,mid,pos);//新建左兒子 else ins(tree[lst].rs,tree[now].rs,mid+1,r,pos);//新建右兒子 updata(now);//更新當前節點信息 } int query(int L,int R,int l,int r,int limit) { if(l==r) { if(tree[R].cnt-tree[L].cnt>limit)return l;//記得判斷 else return 0; } int mid=(l+r)>>1; int tmp1=tree[tree[R].ls].cnt-tree[tree[L].ls].cnt; int tmp2=tree[tree[R].rs].cnt-tree[tree[L].rs].cnt; if(tmp1>limit)return query(tree[L].ls,tree[R].ls,l,mid,limit); if(tmp2>limit)return query(tree[L].rs,tree[R].rs,mid+1,r,limit); return 0;//同題解所述 } }T; int main() { n=read(),m=read(); for(int i=1;i<=n;i++) { int x=read(); T.ins(rt[i-1],rt[i],1,n,x);//rt[i]表示第i個版本的主席樹 } for(int i=1;i<=m;i++) { int l=read(),r=read(),limit=(r-l+1)>>1; int ans=T.query(rt[l-1],rt[r],1,n,limit); printf("%d\n",ans); } return 0; }
BZOJ3524:[POI2014]Couriers