• 【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
    ",ans);
    	return 0;
    }
    

      

  • 相关阅读:
    es6-14 Promise
    微信公众号支付 出现的问题
    RedisDesktopManager 打开报0xc000007b程序错误
    easyui 下对radio的选中操作
    谷歌浏览器(Chrome)禁止浏览器缓存 设置
    oracle 中start with 的用法
    Spring JdbcTemplate 调用存储过程
    Object 转化为String时的一个问题 null->"null"
    android/ios js 启动apk
    nexus2.1.2的配置
  • 原文地址:https://www.cnblogs.com/autsky-jadek/p/4159897.html
Copyright © 2020-2023  润新知