http://www.lydsy.com/JudgeOnline/problem.php?id=2809 (题目链接)

题意

  给出一棵树,每个节点有两个权值${c}$,${L}$,分别代表花费和领导力,在树中找到一个点${i}$,并且找到这个点子树中的一些点组成一个集合,使得集合中的所有点的${c}$之和不超过${M}$,且${L[i]*集合中元素个数和}$最大。

Solution

  听说这道题正解是可并堆,可是并不会做,我们考虑换一种方法。正好最近才学了莫队算法,于是脑洞大开,似乎找到了方法——莫队+分块,是不是很神。

  首先,我们将树上节点按dfs序排序,得到一串序列${S}$,将子树询问转换为在${S}$上的区间询问,总共${n}$棵子树,所以也就只有${n}$个询问,可以承受。

  然后我们发现,对于单一节点来说,“使得集合中的所有点的${c}$之和不超过${M}$,且${Li*集合中元素个数和最大}$”可以贪心,也就是尽量选取${c}$小的节点加入集合。于是我们想到了对权值${c}$离散化后进行分块(注意是权值${c}$而不是序列${S}$),对于无修改区间查询用莫队进行转移,权值分块的修改是${O(1)}$的,查询是${O(\sqrt{n})}$的,复杂度仍是${O(n*\sqrt{n})}$。

  接下来就是细节问题。我们先dfs一遍求出dfs序以及区间之后对区间排序(以左端点所在块作为第一关键字,右端点作为第二关键字),之后对树上的节点按照${C}$的大小排序,求出${C}$的值域记录在${ma[]}$数组中,并用${p}$数组记录离散化后当前节点的编号。对${C}$的值域建块。

  如何插入,删除和询问呢?对于插入和删除,我们用一个数组${b[x]}$来记录在${C}$为${x}$的节点有几个在当前询问中,用${cnts[x]}$来记录${x}$所在的块中有几个节点在当前询问中,用${sumv[x]}$来记录${x}$所在的块中当前的所有${C}$的和。

  有了这三个数组询问就很好做了。

  记得我在糖果公园里面写过:莫队中的分块只是用来保证复杂度,而这里把dfs序抠出来在变成序列上的莫队不是复杂度不对吗?其实不是这样的,糖果公园那道题查询的是树上路径,而这里查询的是子树,两者不是一个东西,这两种询问的范围也不一样。所以这里直接将dfs序抠出来是可以的。

细节

  注意莫队的块与权值分块的区别,别搞混了。

代码

// bzoj2120
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<vector>
#define MOD 1000000007
#define inf 2147483640
#define LL long long
#define free(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);
using namespace std;
inline LL getint() {
LL x=0,f=1;char ch=getchar();
while (ch>'9' || ch<'0') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
return x*f;
} const int maxn=100010;
struct edge {int next,to;}e[maxn<<2];
struct interval {int l,r,id,L;}t[maxn];
struct data {int dfn,c,l;}a[maxn];
int n,m,M,cnt,mo_block,chunk_block;
int L[maxn],head[maxn],cnts[maxn],pos[maxn],sumv[maxn],b[maxn],ma[maxn],p[maxn]; void insert(int u,int v) {
e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
}
void dfs(int u) {
t[u].l=a[u].dfn=++cnt;
for (int i=head[u];i;i=e[i].next) dfs(e[i].to);
t[u].r=cnt;t[u].id=u;t[u].L=a[u].l;
}
bool ccmp(data a,data b) {
return a.c<b.c;
}
bool poscmp(interval a,interval b) {
return pos[a.l]==pos[b.l]?a.r<b.r:pos[a.l]<pos[b.l];
}
void Mo_build() {
mo_block=(int)sqrt(n);
for (int i=1;i<=n;i++) pos[i]=(i-1)/mo_block+1;
}
void chunk_build() {
chunk_block=(int)sqrt(cnt);
m=(cnt%chunk_block>0) ? cnt/chunk_block+1 : cnt/chunk_block;
for (int i=1;i<=cnt;i++) pos[i]=(i-1)/chunk_block+1;
for (int i=1;i<=m;i++) L[i]=(i-1)*chunk_block+1;
}
void update(int x,int val) {
b[x]+=val;
cnts[pos[x]]+=val;
sumv[pos[x]]+=(LL)val*ma[x];
}
int query() {
LL tot=0;int ans=0;
for (int i=1;i<=m;i++) {
tot+=sumv[i];ans+=cnts[i];
if (tot>M) {
tot-=sumv[i];ans-=cnts[i];
for (int j=L[i];;j++) {
tot+=(LL)ma[j]*b[j];
ans+=b[j];
if (tot>M) return ans-(int)(tot-M)/ma[j]-((tot-M)%ma[j]!=0);
}
}
}
return ans;
}
int main() {
scanf("%d%d",&n,&M);
for (int i=1;i<=n;i++) {
int x;
scanf("%d%d%d",&x,&a[i].c,&a[i].l);
insert(x,i);
}
cnt=0;
dfs(e[head[0]].to);
Mo_build();
sort(t+1,t+1+n,poscmp);
for (int i=1;i<=n;i++) pos[i]=0;
sort(a+1,a+1+n,ccmp);
cnt=0;ma[p[a[1].dfn]=++cnt]=a[1].c;
for (int i=2;i<=n;i++) {
if (a[i].c!=a[i-1].c) cnt++;
ma[p[a[i].dfn]=cnt]=a[i].c;
}
LL ans=0;
chunk_build();
for (int l=1,r=0,i=1;i<=n;i++) {
for (;r<t[i].r;r++) update(p[r+1],1);
for (;r>t[i].r;r--) update(p[r],-1);
for (;l<t[i].l;l++) update(p[l],-1);
for (;l>t[i].l;l--) update(p[l-1],1);
ans=max(ans,(LL)query()*t[i].L);
}
printf("%lld\n",ans);
return 0;
}

UPD 2017.1.9

Solution

  可并堆。

  对于每个节点建立一个大根堆,如果堆中所有元素之和大于M,那么pop堆顶。每次dfs合并当前节点的堆与儿子节点的堆。

代码

// bzoj2809
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=100010;
struct edge {int to,next;}e[maxn];
struct heap {int val,l,r;}q[maxn];
int head[maxn],cnt,sz,m,n;
int size[maxn],C[maxn],rt[maxn];
LL L[maxn],sum[maxn],ans; void link(int u,int v) {
e[++cnt]=(edge){v,head[u]};head[u]=cnt;
}
int merge(int x,int y) {
if (x==0 || y==0) return x+y;
if (q[x].val<q[y].val) swap(x,y);
q[x].r=merge(q[x].r,y);
swap(q[x].l,q[x].r);
return x;
}
int pop(int x) {
return merge(q[x].l,q[x].r);
}
void dfs(int x) {
rt[x]=++sz;size[x]=1;
q[sz].val=sum[x]=C[x];
for (int i=head[x];i;i=e[i].next) {
dfs(e[i].to);
sum[x]+=sum[e[i].to];
size[x]+=size[e[i].to];
rt[x]=merge(rt[x],rt[e[i].to]);
}
while (sum[x]>m) {
sum[x]-=q[rt[x]].val;
rt[x]=pop(rt[x]);
size[x]--;
}
ans=max(ans,size[x]*L[x]);
}
int main() {
scanf("%d%d",&n,&m);
for (int x,i=1;i<=n;i++) {
scanf("%d",&x);
link(x,i);
scanf("%d%lld",&C[i],&L[i]);
}
dfs(1);
printf("%lld",ans);
return 0;
}

【bzoj2809】 Apio2012—dispatching的更多相关文章

  1. 【BZOJ2809】[Apio2012]dispatching 可并堆

    [BZOJ2809][Apio2012]dispatching Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 M ...

  2. 【bzoj2809】[Apio2012]dispatching 左偏树

    2016-05-31  15:56:57 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2809 直观的思想是当领导力确定时,尽量选择薪水少的- ...

  3. 【bzoj2809】[Apio2012]dispatching 贪心+可并堆

    题目描述 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增 ...

  4. 【BZOJ2809】[APIO2012] dispatching(左偏树例题)

    点此看题面 大致题意: 有\(N\)名忍者,每名忍者有三个属性:上司\(B_i\),薪水\(C_i\)和领导力\(L_i\).你要选择一个忍者作为管理者,然后在所有被他管理的忍者中选择若干名忍者,使薪 ...

  5. 【bzoj2809】[Apio2012]dispatching (左偏树)

    我们需要枚举根,然后从其子树内选尽量多的点,薪水不超过M,可是暴力复杂度不对.于是考虑自下而上合并树(开始每棵树内只有一个节点,就是自己) 每个树是一个堆,我们维护树的节点个数和薪水总和,合并时,不断 ...

  6. 【APIO2012】【BZOJ2809】派遣dispatching

    2809: [Apio2012]dispatching Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1932 Solved: 967 [Submit ...

  7. 【BZOJ-2809】dispatching派遣 Splay + 启发式合并

    2809: [Apio2012]dispatching Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2334  Solved: 1192[Submi ...

  8. 【BZOJ2809】【splay启发式合并】dispatching

    Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级. ...

  9. 【BZOJ2809】【APIO2012】Dispatching(左偏树)

    题面 Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个 ...

随机推荐

  1. win7 IIS7.5配置伪静态

    转自:http://www.cnblogs.com/luckly-hf/archive/2013/03/07/2947687.html 第一部: 从如下地址中下载URLRewriter组件组件: 官方 ...

  2. BZOJ 2733 【HNOI2012】 永无乡

    Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以 ...

  3. Protocol https not supported or disabled in libcurl

    最后用PHP Curl 模拟访问HTTPS ,总是得到 Protocol https not supported or disabled in libcurl 错误,奇怪了,找了很多资料,有人说没有开 ...

  4. Camera.Parameters 参数

    public class Camera.Parameters extends Object java.lang.Object    ↳ android.hardware.Camera.Paramete ...

  5. I/O 流和对象序列化

    一.I/O 流(java 如何实现与外界数据的交流) 流定义: 任何有能力产出数据的数据源对象或者有能力接收数据的数据源对象.他屏蔽了实际的I/O设备处理数据的细节. 1.Input/Output:指 ...

  6. 利用javascript对提交数据验证

    优点:提交前验证.在客户端进行. <html> <head> <script language="javascript"> function c ...

  7. Theano2.1.17-基础知识之剖析theano的函数

    来自:http://deeplearning.net/software/theano/tutorial/profiling.html Profiling Theano function note:该方 ...

  8. .NET Core VS Code 环境配置

    VSCode .NET环境配置     在此之前我一直是使用notepad++配置的C/C#环境来写代码,比起打开"笨重"的VS要方便很多.VSCode出来之后,本来也想折腾了一下 ...

  9. HttpClient4.5 SSL访问工具类

    要从网上找一个HttpClient SSL访问工具类太难了,原因是HttpClient版本太多了,稍有差别就不能用,最后笔者干脆自己封装了一个访问HTTPS并绕过证书工具类. 主要是基于新版本Http ...

  10. 网络功能虚拟化(NFV)

    你造什么是网络功能虚拟化(NFV)吗? NFV将网络功能整合到行业标准的服务器.交换机和存储硬件上,提供了优化的虚拟化数据平面,NFV通过服务器上运行的软件让管理员取代传统物理网络设备,并降低成本.能 ...