[線段樹]CF674 G - Choosing Ads
阿新 • • 發佈:2020-12-30
如果 \(p>50\),那麼這個問題就是一個經典的眾數問題,有一個 \(O(n)\) 的做法:維護一個二元組 \((w,c)\),遇到一個數 \(x\),若 \(x=w\),++\(c\),否則 --\(c\)。當 \(c\) 恰好減到 \(0\) 時,二元組變為 \((x,1)\) 然後接著做下去,最後二元組裡的數 \(w\) 就是眾數。因為一個眾數抵消一個其他的數,抵消完後,最後一定還剩下至少一個眾數。
我們想辦法把這個演算法拓展到 \(p\) 更小的情況,其實我們發現 \(p\) 的大小跟最多允許存在多少個眾數成反比,即 \(\lfloor\frac{100}{p}\rfloor\)。換作這道題,就變成了求出現次數前這麼多大的數。套用上面的演算法試試:維護 \(\lfloor\frac{100}{p}\rfloor\)
這題涉及區間操作,所以考慮用線段樹來維護,每個結點掛幾個二元組。現在的問題就變為怎麼合併兩個區間的二元組集合。列舉一個集合中的二元組 \((w,c)\),則:
- 若 \(w\) 出現過,直接把 \(c\) 貢獻上去即可。
- 否則若另一個集合的大小 \(< \lfloor\frac{100}{p}\rfloor\)
- 否則若另一個集合裡 \(c\) 的最小值大於當前這個二元組的 \(c\) 值,則將集合內所有的 \(c\) 值減去當前二元組的 \(c\) 值。
- 否則將 \(c\) 值最小的二元組替換為當前二元組,並將集合內所有的 \(c\) 值減去被替換掉的那個二元組的 \(c\) 值。
複雜度 \(O(n\; \log n\; (\lfloor\frac{100}{p}\rfloor)^2)\),跑不滿,可過。
code below:
#include<iostream> #include<cstdio> #include<vector> using namespace std; const int N=1002020; int n,p; int wt[N],tag[N<<2],vis[20]; struct node{ int w,c; node(int w1=0,int c1=0){w=w1;c=c1;}; }; vector<node> tr[N<<2],res,tmp; vector<node> merge(int x,int y){ // swap(x,y); // cout<<"merge:\n"; // for(int i=0;i<tr[x].size();i++)cout<<tr[x][i].w<<" ";cout<<endl; // for(int i=0;i<tr[y].size();i++)cout<<tr[y][i].w<<" ";cout<<endl; /* res=tr[y]; for(int i=0;i<tr[x].size();i++){ bool fl=1; int mi=2e9; for(int j=0;j<res.size() && fl;j++){ if(res[j].w==tr[x][i].w) res[j].c+=tr[x][i].c,fl=0; mi=min(mi,res[j].c); } if(!fl)continue; if(res.size()<100/p)res.push_back(tr[x][i]); else if(tr[x][i].c<mi){ // cout<<"in1"<<endl; for(int i=0;i<res.size();i++) res[i].c-=tr[x][i].c; } else{ // cout<<"in2:"<<mi<<endl; res.push_back(tr[x][i]); tmp.clear(); for(int i=0;i<res.size();i++) if(res[i].c!=mi)tmp.push_back(res[i]); swap(res,tmp); } }*/ // for(int i=0;i<res.size();i++)cout<<res[i].w<<" ";cout<<endl; res.clear(); tmp=tr[x]; for(int i=0;i<tr[y].size();i++){ int fl=0; for(int j=0;j<tmp.size();j++) if(tr[y][i].w==tmp[j].w){tmp[j].c+=tr[y][i].c;fl=1;break;} if(!fl)tmp.push_back(tr[y][i]); } for(int i=0;i<tmp.size();i++){ if(res.size()<100/p) res.push_back(tmp[i]); else{ int pos=0; for(int j=0;j<res.size();j++) if(res[j].c<res[pos].c)pos=j; if(res[pos].c>tmp[i].c){ for(int j=0;j<res.size();j++) res[j].c-=tmp[i].c; } else{ int d=res[pos].c; res[pos]=tmp[i]; for(int j=0;j<res.size();j++)res[j].c-=d; } } } return res; } void build(int k,int l,int r){ if(l==r)return tr[k].push_back(node(wt[l],1)),void(); int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); tr[k]=merge(k<<1,k<<1|1); } void down(int k,int l,int r){ if(tag[k]){ int mid=(l+r)>>1; tr[k<<1].clear(); tr[k<<1|1].clear(); tr[k<<1].push_back(node(tr[k][0].w,mid-l+1)); tr[k<<1|1].push_back(node(tr[k][0].w,r-mid)); tag[k<<1]=tag[k<<1|1]=1; tag[k]=0; } } void change(int k,int l,int r,int ql,int qr,int w){ if(ql<=l && qr>=r){ tr[k].clear(); tr[k].push_back(node(w,r-l+1)); tag[k]=1; return; } down(k,l,r); int mid=(l+r)>>1; if(ql<=mid)change(k<<1,l,mid,ql,qr,w); if(qr>mid)change(k<<1|1,mid+1,r,ql,qr,w); tr[k]=merge(k<<1,k<<1|1); //updata(k); } void query(int k,int l,int r,int ql,int qr){ if(ql<=l && qr>=r) return tr[0]=merge(0,k),void(); down(k,l,r); int mid=(l+r)>>1; if(ql<=mid)query(k<<1,l,mid,ql,qr); if(qr>mid)query(k<<1|1,mid+1,r,ql,qr); } int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } /*void print(int k,int l,int r){ cout<<k<<" ["<<l<<","<<r<<"] : "; for(int i=0;i<tr[k].size();i++)cout<<tr[k][i].w<<"("<<tr[k][i].c<<") ";cout<<endl; if(l==r)return; int mid=(l+r)>>1; down(k,l,r); print(k<<1,l,mid); print(k<<1|1,mid+1,r); }*/ int main() { n=read(); int aq=read(); p=read(); for(int i=1;i<=n;i++)wt[i]=read(); build(1,1,n); while(aq--){ int op=read(),l=read(),r=read(); if(op==1)change(1,1,n,l,r,read()); else{ tr[0].clear(); query(1,1,n,l,r); printf("%d ",tr[0].size()); for(int i=0;i<tr[0].size();i++) printf("%d ",tr[0][i].w); putchar('\n'); } // print(1,1,n); } return 0; } /* 10 5 50 5 1 3 2 4 1 1 4 2 1 1 5 7 3 1 7 8 6 1 8 9 5 1 3 5 5 2 7 10 5 1 5 5 5 3 6 5 5 1 */