【DFS序】【莫队算法】【权值分块】bzoj2809 [Apio2012]dispatching
题意:在树中找到一个点i,并且找到这个点子树中的一些点组成一个集合,使得集合中的所有点的c之和不超过M,且Li*集合中元素个数和最大
首先,我们将树处理出dfs序,将子树询问转化成区间询问。
然后我们发现,对于单一节点来说,“使得集合中的所有点的c之和不超过M,且Li*集合中元素个数和最大”可以贪心地搞,即优先选择c较小的点。(<--这正是主席树/权值线段树/权值分块的工作)
但是我们需要枚举所有节点,从他们中选一个最大的。
既然有dfs序了,那么就是无修改的区间询问咯。(<--莫队的工作) 但是莫队转移的过程中,主席树/权值线段树的插入/删除无法承受。(当然主席树根本就用不着莫队,也可以解决这个问题,但这不是这篇文章要介绍的) 权值分块的插入/删除是O(1)的,查询是O(sqrt(n))的,总复杂度仍是O(n*sqrt(n))的。
编程复杂度较低,常数较小。
#include<cstdio> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; ll sumv[330],m,ans; int num2[100001],num[100001],l[330],cnts[330],b[100001],sum=1; struct Point{int v,p;}t[100001]; struct Ask{int l,r,p;}Q[100001]; bool operator < (const Point &a,const Point &b){return a.v<b.v;} bool operator < (const Ask &a,const Ask &b) {return num2[a.l]!=num2[b.l] ? num2[a.l]<num2[b.l] : a.r<b.r;} int v[100001],first[100001],next[100001]; int n,es,p,w[100001],w2[100001],L,a[100001],ma[100001],en,lead[100001]; void AddEdge(const int &U,const int &V) { v[++es]=V; next[es]=first[U]; first[U]=es; } void dfs(int U) { Q[U].l=++L; t[L].v=w[U]; t[L].p=L; lead[L]=w2[U]; for(int i=first[U];i;i=next[i]) dfs(v[i]); Q[U].r=L; Q[U].p=U; } void Val_Make_Block() { int sz=sqrt(en); if(!sz) sz=1; for(;sum*sz<en;++sum) { l[sum]=(sum-1)*sz+1; int r=sum*sz; for(int i=l[sum];i<=r;++i) num[i]=sum; } l[sum]=(sum-1)*sz+1; for(int i=l[sum];i<=en;++i) num[i]=sum; } void Mo_Make_Block() { int sum=1,sz=sqrt(n); if(!sz) sz=1; for(;sum*sz<n;++sum) { int r=sum*sz; for(int i=(sum-1)*sz+1;i<=r;++i) num2[i]=sum; } for(int i=(sum-1)*sz+1;i<=n;++i) num2[i]=sum; } void Insert(const int &x){++b[x]; ++cnts[num[x]]; sumv[num[x]]+=(ll)ma[x];} void Delete(const int &x){--b[x]; --cnts[num[x]]; sumv[num[x]]-=(ll)ma[x];} int Query() { ll tot=0; int res=0; for(int i=1;i<=sum;++i) { tot+=sumv[i]; res+=cnts[i]; if(tot>m) { tot-=sumv[i]; res-=cnts[i]; for(int j=l[i];;++j) { tot+=((ll)ma[j]*(ll)b[j]); res+=b[j]; if(tot>m) return res-(int)((tot-m)/ma[j])-((tot-m)%ma[j]!=0); } } } return res; } int main() { scanf("%d%lld",&n,&m); for(int i=1;i<=n;++i) { scanf("%d%d%d",&p,&w[i],&w2[i]); AddEdge(p,i); } dfs(1); sort(t+1,t+n+1); ma[a[t[1].p]=++en]=t[1].v; for(int i=2;i<=n;++i) { if(t[i].v!=t[i-1].v) ++en; ma[a[t[i].p]=en]=t[i].v; } Val_Make_Block(); Mo_Make_Block(); sort(Q+1,Q+n+1); for(int i=Q[1].l;i<=Q[1].r;++i) Insert(a[i]); ans=max(ans,(ll)Query()*(ll)lead[Q[1].l]); for(int i=2;i<=n;++i) { if(Q[i].l<Q[i-1].l) for(int j=Q[i-1].l-1;j>=Q[i].l;--j) Insert(a[j]); else for(int j=Q[i-1].l;j<Q[i].l;++j) Delete(a[j]); if(Q[i].r<Q[i-1].r) for(int j=Q[i-1].r;j>Q[i].r;--j) Delete(a[j]); else for(int j=Q[i-1].r+1;j<=Q[i].r;++j) Insert(a[j]); ans=max(ans,(ll)Query()*(ll)lead[Q[i].l]); } printf("%lld\n",ans); return 0; }
——The Solution By AutSky_JadeK From UESTC
转载请注明出处:http://www.cnblogs.com/autsky-jadek/