来自FallDream的博客,未经允许,请勿转载,谢谢。

---------------------------------------------------

A.dispatching 派遣

上次hzwer出了这道题所以做过了,搬一下以前的博文。

给定一棵n个点的树和一个费用m,每个点有一个忍者,派遣它的费用是ci,它的领导力是li。你要选择一个点作为领导,并且在它的子树中(包括它)选出尽可能多的点,满足费用不超过m且选出的点的数量*领导的领导力最大。n<=100000  m,c,l<=10^9

题解:这道题很多做法吧..首先费用少的肯定先选,我们每次肯定是从费用小的开始选,所以题目可以转换为求所有点的子树中最多能选几个点。

做法1:平衡树/优先队列+启发式合并

把费用装进一个平衡树/优先队列里面,然后启发式合并,合并次数是nlogn,总复杂度nlog^2n

我的做法:树剖那样子标号,满足子树的dfs序连续,然后主席树,复杂度是nlogn

#include<iostream>
#include<cstdio>
#include<queue>
#define MN 100000
#define MM 5000000
#define INF 2000000000
#define ll long long
using namespace std;
inline int read()
{
int x = , f = ; char ch = getchar();
while(ch < '' || ch > ''){ if(ch == '-') f = -; ch = getchar();}
while(ch >= '' && ch <= ''){x = x * + ch - '';ch = getchar();}
return x * f;
} int n,dfn=,cnt=,mx[MN+],size[MN+],nl[MN+],nr[MN+],head[MN+],id[MN+],rt[MN+];
ll c[MN+],l[MN+],ans=,m;
struct edge{
int to,next;
}e[MN+];
struct TREE{
int l,r,size;ll x;
}T[MM+]; void ins(int f,int t)
{
e[++cnt]=(edge){t,head[f]};head[f]=cnt;
} void dfs1(int x)
{
size[x]=;mx[x]=;int maxn=;
for(int i=head[x];i;i=e[i].next)
{
dfs1(e[i].to);
size[x]+=size[e[i].to];
if(size[e[i].to]>maxn){maxn=size[e[i].to];mx[x]=e[i].to;}
}
} void dfs2(int x)
{
nl[x]=++dfn;id[dfn]=x;
if(mx[x]) dfs2(mx[x]);
for(int i=head[x];i;i=e[i].next)
if(e[i].to!=mx[x])
dfs2(e[i].to);
nr[x]=dfn;
} void ins(int x,int nx,ll c)
{
T[nx].size=T[x].size+;T[nx].x=T[x].x+c;
ll l=,r=INF,mid;
while(l<r)
{
mid=l+r>>;
if(c<=mid)
{
T[nx].r=T[x].r;T[nx].l=++cnt;
r=mid;nx=T[nx].l;x=T[x].l;
}
else
{
T[nx].l=T[x].l;T[nx].r=++cnt;
l=mid+;nx=T[nx].r;x=T[x].r;
}
T[nx].size=T[x].size+;T[nx].x=T[x].x+c;
}
} int query(int x,int nx,ll cc)
{
ll l=,r=INF,mid,num=;
while(l<r)
{
mid=l+r>>;
if(T[T[nx].l].x-T[T[x].l].x<=cc)
{
cc-=T[T[nx].l].x-T[T[x].l].x;
num+=T[T[nx].l].size-T[T[x].l].size;
x=T[x].r;nx=T[nx].r;l=mid+;
}
else
{
x=T[x].l;nx=T[nx].l;
r=mid;
}
}
if(T[nx].size-T[x].size>)
{
int x=cc/((T[nx].x-T[x].x)/(T[nx].size-T[x].size));
num+=x;
}
return num;
} main()
{ n=read();m=read();
for(int i=;i<=n;i++)
{
int fa=read();c[i]=read();l[i]=read();
if(fa) ins(fa,i);
}
dfs1();dfs2();cnt=;
for(int i=;i<=n;i++)
ins(rt[i-],rt[i]=++cnt,c[id[i]]);
for(int i=;i<=n;i++)
ans=max(ans,l[i]*1LL*query(rt[nl[i]-],rt[nr[i]],m));
cout<<ans;
return ;
}

B.guard守卫

有n个草丛(#滑稽),里面有K个盖伦忍者,有m个条信息,表示一段区间内有没有盖伦忍者,你要求出一定有盖伦忍者的草丛。   $n,m,k\leqslant 10^{5}$

一开始我看到这道题就写了一个线段树+大判断,挂了3个点,想了好久才发现忍者数量可能会超过的情况没判。

讲讲正解:先删掉没用的,然后剩下的如果是K个,就直接输出,否则我们重新标号之后,排序,删除无用区间,这样满足信息的左右坐标都递增。很显然,对每段区间选择放在最右边的点是最优的。用F[i]表示前i个区间放在最右的节点至少放几个,g[i]表示后... 然后对每个区间,如果它长度为一,直接输出,否则我们认为放在第二右边是次优的,二分出不能覆盖第二右的节点的区间范围,假设是1..a 和b..m,那么判断f[a]+g[b]+1如果大于K,说明这个最右的节点必须放,否则会超过K个。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define MN 100000
using namespace std;
inline int read()
{
int x = , f = ; char ch = getchar();
while(ch < '' || ch > ''){ if(ch == '-') f = -; ch = getchar();}
while(ch >= '' && ch <= ''){x = x * + ch - '';ch = getchar();}
return x * f;
}
struct TREE{int l,r,val,x;}T[MN*+];
struct ques{int l,r;
bool operator <(const ques&y)const{return l==y.l?r>y.r:l<y.l;}
}q[MN+],s[MN+];
int n,K,m,cnt=,f[MN+],g[MN+],nl[MN+],nr[MN+],cn=,id[MN+]; void build(int x,int l,int r)
{
if((T[x].l=l)==(T[x].r=r)) {T[x].x=;return;}
int mid=l+r>>;
build(x<<,l,mid);
build(x<<|,mid+,r);
T[x].x=T[x<<].x+T[x<<|].x;
} void modify(int x,int l,int r)
{
if(T[x].val)return;
if(T[x].l==l&&T[x].r==r)
{
T[x].val=;T[x].x=;
return;
}
int mid=T[x].l+T[x].r>>;
if(r<=mid) modify(x<<,l,r);
else if(l>mid) modify(x<<|,l,r);
else {modify(x<<,l,mid);modify(x<<|,mid+,r);}
T[x].x=T[x<<].x+T[x<<|].x;
} int query(int x,int k)
{
if(T[x].val)return ;
if(T[x].l==T[x].r)return T[x].x;
int mid=T[x].l+T[x].r>>;
return k<=mid?query(x<<,k):query(x<<|,k);
} int get(int x,int l,int r)
{
int ans=;
for(int mid=l+r>>;l<=r;mid=l+r>>)
if(s[mid].r<x) ans=mid,l=mid+;
else r=mid-;
return f[ans];
} int get2(int x,int l,int r)
{
int ans=;
for(int mid=l+r>>;l<=r;mid=l+r>>)
if(s[mid].l>x) ans=mid,r=mid-;
else l=mid+;
return g[ans];
} int main()
{
n=read();K=read();m=read();
build(,,n);
for(int i=;i<=m;i++)
{
int l=read(),r=read(),x=read();
if(!x) modify(,l,r);
else q[++cnt]=(ques){l,r};
}
if(T[].x==K)
{
for(int i=;i<=n;i++)
if(query(,i)==)
printf("%d\n",i);
return ;
}
for(int i=;i<=n;i++)
if(query(,i)==)
nl[i]=++cn,id[cn]=i;
for(int i=n;i;i--)
nr[i]=(nl[i]?nl[i]:nr[i+]);
for(int i=;i<=n;i++)
nl[i]=(nl[i]?nl[i]:nl[i-]);
for(int i=;i<=cnt;i++)
q[i].l=nr[q[i].l],q[i].r=nl[q[i].r];
sort(q+,q+cnt+);int j=;
for(int i=;i<=cnt;i++)
{
while(j&&q[i].r<=s[j].r)--j;
s[++j]=q[i];
}
for(int i=,mx=;i<=j;i++)
if(s[i].l>mx) {mx=s[i].r;f[i]=f[i-]+;}
else f[i]=f[i-];
for(int k=j,mn=n+;k;k--)
if(s[k].r<mn) {mn=s[k].l;g[k]=g[k+]+;}
else g[k]=g[k+];
bool flag=;
for(int i=;i<=j;i++)
{
if(f[i]!=f[i-]+) continue;
if(s[i].l==s[i].r) {flag=;printf("%d\n",id[s[i].l]);continue;}
int lt=get(s[i].r-,,i-),rt=get2(s[i].r-,i+,j);
if(lt+rt+>K) printf("%d\n",id[s[i].r]),flag=;
}
if(!flag) puts("-1");
return ;
}

C.苦无

大概就是乱排序每种情况考虑一下,然后堆搞一搞,最后矩形面积并。

傻逼大码农,本来打算码的,现在代码已经被我删了.

[APIO2012]的更多相关文章

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

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

  2. APIO2012派遣

    2809: [Apio2012]dispatching Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1196  Solved: 586[Submit ...

  3. 数据结构,可并堆(左偏树):COGS [APIO2012] 派遣

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

  4. BZOJ 2809: [Apio2012]dispatching( 平衡树 + 启发式合并 )

    枚举树上的每个结点做管理者, 贪心地取其子树中薪水较低的, 算出这个结点为管理者的满意度, 更新答案. 用平衡树+启发式合并, 时间复杂度为O(N log²N) ------------------- ...

  5. [Apio2012]dispatching

    [Apio2012]dispatching 时间限制: 1 Sec  内存限制: 128 MB 题目描述 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一 ...

  6. bzoj2811[Apio2012]Guard 贪心

    2811: [Apio2012]Guard Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 905  Solved: 387[Submit][Statu ...

  7. BZOJ_2809_[Apio2012]dispatching_可并堆

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

  8. bzoj2809 [Apio2012]dispatching(左偏树)

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

  9. [APIO2012]派遣

    [APIO2012]派遣 题目大意: 给定一棵\(n(n\le10^5)\)个结点的有根树,每个点有代价\(c_i\)和权值\(l_i\),要求你选定一个结点\(k\),并在对应的子树中选取一个点集\ ...

  10. [luogu P1552] [APIO2012]派遣

    [luogu P1552] [APIO2012]派遣 题目背景 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿. 题目描述 在这个帮派里,有一名忍者被称之为Master.除 ...

随机推荐

  1. 0基础菜鸟学前端之Vue.js

    简介:0基础前端菜鸟,啃了将近半月前端VUE框架,对前端知识有了初步的了解.下面总结一下这段时间的学习心得. 文章结构 前端基础 Vue.js简介 Vue.js常用指令 Vue.js组件 Vue.js ...

  2. 从数据恢复角度解析RAID6结构原理

    [什么是RAID]    RAID的概念描述在互联网上比比皆是,用最简单的原理描述,就是在定义存储方式时允许在一部分数据缺失的情况下不影响全部数据,类似于通讯领域的纠错码.不同的冗余模式形成了不同的R ...

  3. ebtables和iptables与linux bridge的交互

    本文为翻译文,不一定是逐字逐句的翻译,而且中间会加上自己的一点见解,如有理解错误的地方,还请大家指出,我定虚心学习.原文见链接 其中斜体字是自己的理解,建议和ebtables手册和iptables手册 ...

  4. Android广播发送失败

    现在至今为止Android 8.0 不支持大部分广播收发 如果无法使用建议换至Android 7.0版本 且 minSdkVersion 24

  5. java的<<左移,>>右移,>>>无符号右移

    >>右移 右移,道在二进制中,假设用一个32位的Int表示一个64,那么高位就都是0,所以当我们把整个二进制数右移,如0100000 >> 2 = 0001000,可以看到右移 ...

  6. hadoop大数据技术架构详解

    大数据的时代已经来了,信息的爆炸式增长使得越来越多的行业面临这大量数据需要存储和分析的挑战.Hadoop作为一个开源的分布式并行处理平台,以其高拓展.高效率.高可靠等优点越来越受到欢迎.这同时也带动了 ...

  7. jenkins 简单实现php集成上线部署

    基于公司git版本控制,搭建jenkins实现php集成部署(没有用gitlab,测试服配置较低,gitlab卡的不要不要的了-) 一.安装jenkins相关依赖 wget -O /etc/yum.r ...

  8. Linux入门:增加用户,并赋予权限

    一.增加用户 1.增加用户,并指定主目录 # useradd –d /usr/sam -m sam此命令创建了一个用户sam,其中-d和-m选项用来为登录名sam产生一个主目录/usr/sam(/us ...

  9. python网络爬虫与信息提取 学习笔记day3

    Day3: 只需两行代码解析html或xml信息    具体代码实现:day3_1    注意BeautifulSoup的B和S需要大写,因为python大小写敏感 import requests r ...

  10. RxJava系列2(基本概念及使用介绍)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...