[BZOJ2809&1455&1367]解题报告|可并堆

  其实非常好理解..就是可以可以合并起来的两个堆嘛><

2809: [Apio2012]dispatching

Description

在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。在这个帮派里,有一名忍者被称之为 Master。除了 Master以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者 发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递 人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,就不需要支付管理者的薪水。你的目标是在预算内使顾客的满意度最大。这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。写一个程序,给定每一个忍者 i的上级 Bi,薪水Ci,领导力L i,以及支付给忍者们的薪水总预算 M,输出在预算内满足上述要求时顾客满意度的最大值。

1  ≤N ≤ 100,000 忍者的个数;

1  ≤M ≤ 1,000,000,000 薪水总预算;

0  ≤Bi < i  忍者的上级的编号;

1  ≤Ci ≤ M                     忍者的薪水;

1  ≤Li ≤ 1,000,000,000             忍者的领导力水平。

  

  嗯这道题XJOI上有然后当时并没有做...看到满屏的splay吓傻

  题意大概就是求出每个节点子树中权值加和不大于一个给定值m的最多个数,也就是尽量取小的

  之前并不会可并堆..然后脑补了一个每次msort的递归做法,T掉了

  实际上我们可以反过来考虑,假设一个节点u的子树的大小为sz,在薪水总数不超过m的情况下能取x个,

  也就是有sz-x个数在这个节点的答案中是用不到的

  实际上在u以上的节点也用不到,有了这个性质我们就可以用可并堆然后每次不停地把子树中最大的删去直到满足sum<=m

/**************************************************************
    Problem: 2809
    User: mjy0724
    Language: C++
    Result: Accepted
    Time:1284 ms
    Memory:9188 kb
****************************************************************/

#include<cstdio>
#include<cstdlib>
#include<cstring>
#define ll long long
#define maxn 100010
struct node{
    int l,r;
    ll sum,sz;
}a[maxn];
ll n,e,c[maxn],root[maxn],fa[maxn],next[maxn],link[maxn];
ll ans,l[maxn],k;
ll max(ll x,ll y){
    if (x>y) return x;return y;
}
void swap(int &x,int &y){
    int tmp=x;x=y;y=tmp;
}
int merge(int x,int y){
    if (x==0||y==0) return x+y;
    if (c[x]<c[y]) swap(x,y);
    a[x].r=merge(a[x].r,y);
    swap(a[x].l,a[x].r);
    return x; //这条语句经常忘记写啊tat!
}
void add(int x,int y){
    fa[++e]=y;next[e]=link[x];link[x]=e;
}
void dfs(int p){
    a[p].sum=c[p];a[p].sz=1;root[p]=p;
    for (int i=link[p];i;i=next[i]){
        dfs(fa[i]);
        a[p].sum+=a[fa[i]].sum;
        a[p].sz+=a[fa[i]].sz;
        root[p]=merge(root[p],root[fa[i]]);
    }
    while (a[p].sum>k){
        a[p].sum-=c[root[p]];a[p].sz--;
        root[p]=merge(a[root[p]].l,a[root[p]].r);
    }
    ans=max(ans,(ll)a[p].sz*l[p]);//边dfs边更新答案
}
int main(){
    scanf("%lld%lld",&n,&k);
    e=0;ans=0;int x;
    for (int i=1;i<=n;i++) scanf("%d%lld%lld",&x,&c[i],&l[i]),add(x,i);
    dfs(1);
    printf("%lld\n",ans);
}


  

1455: 罗马游戏

Description

罗马皇帝很喜欢玩杀人游戏。 他的军队里面有n个人,每个人都是一个独立的团。最近举行了一次平面几何测试,每个人都得到了一个分数。 皇帝很喜欢平面几何,他对那些得分很低的人嗤之以鼻。他决定玩这样一个游戏。 它可以发两种命令: 1. Merger(i, j)。把i所在的团和j所在的团合并成一个团。如果i, j有一个人是死人,那么就忽略该命令。 2. Kill(i)。把i所在的团里面得分最低的人杀死。如果i这个人已经死了,这条命令就忽略。 皇帝希望他每发布一条kill命令,下面的将军就把被杀的人的分数报上来。(如果这条命令被忽略,那么就报0分)

  也是非常裸的一道题...kill操作的时候不仅要把当前root的fa改成新合并之后的根

  新合并之后的根的fa也要及时改掉不然会死循环

  其他的就和上一题差不多了..往往要和并查集结合起来做

  然后这道题加了两行成了左偏树

 1 /**************************************************************
 2     Problem: 1455
 3     User: mjy0724
 4     Language: C++
 5     Result: Accepted
 6     Time:2320 ms
 7     Memory:21312 kb
 8 ****************************************************************/
 9
10 #include<cstdio>
11 #include<cstdlib>
12 #include<cstring>
13 #define maxn 1000010
14 struct arr{
15     int ave,l,r;
16 }a[maxn];
17 int fa[maxn],n,m,d[maxn];
18 bool vis[maxn];
19 char s[100];
20 int getfa(int x){
21     if (fa[x]==x) return x;
22     fa[x]=getfa(fa[x]);
23     return fa[x];
24 }
25 void swap(int &x,int &y){
26     int tem=x;x=y;y=tem;
27 }
28 int merge(int x,int y){
29     if (x==0||y==0) return x+y;
30     if (a[y].ave<a[x].ave) swap(x,y);
31     a[x].r=merge(a[x].r,y);
32     if (d[a[x].l]<d[a[x].r]) swap(a[x].l,a[x].r);
33     d[x]=d[a[x].r]+1;
34     return x;
35 }
36 void mer(int x,int y){
37     if (vis[x]==false||vis[y]==false||getfa(x)==getfa(y)) return;
38     x=getfa(x);y=getfa(y);
39     fa[x]=fa[y]=merge(x,y);
40 }
41 int sol(int x){
42     if (vis[x]==false) return 0;
43     int tmp=a[getfa(x)].ave;
44     vis[fa[x]]=false;
45     int y=merge(a[fa[x]].l,a[fa[x]].r);
46     fa[fa[x]]=y;fa[y]=y;
47     return tmp;
48 }
49 int main(){
50     scanf("%d",&n);
51     for (int i=1;i<=n;i++) scanf("%d",&a[i].ave);
52     d[0]=-1;scanf("%d",&m);
53     memset(vis,true,sizeof(vis));
54     for (int i=1;i<=n;i++) fa[i]=i;
55     char ch[10];int x,y;
56     for (int i=1;i<=m;i++){
57         scanf("%s",ch);
58         if (ch[0]==‘M‘) scanf("%d%d",&x,&y),mer(x,y);
59         else scanf("%d",&x),printf("%d\n",sol(x));
60     }
61     return 0;
62 }

1367: [Baltic2004]sequence

Description

  这道题的结论证明参见论文

  对于求不下降序列最后的做法就是:维护几段连续的序列,使它们的中位数不下降

  然而转化到递增序列,我们只需要将每个数读进来的之后减去它的下标就可以了

  所以我们对于每一段已求好的序列,既要维护它的中位数,又要支持合并

  因为我们合并的前提是:中位数(i)>中位数(i+1),那么对于合并后的i而言,中位数肯定是不升的

  根据这个性质我们又可以用可并堆了,堆顶元素表示该序列中的中位数

  当堆的元素个数*2>序列长度+1的时候就可以弹出堆顶

  

 1 /**************************************************************
 2     Problem: 1367
 3     User: mjy0724
 4     Language: C++
 5     Result: Accepted
 6     Time:8440 ms
 7     Memory:35960 kb
 8 ****************************************************************/
 9
10 #include<cstdio>
11 #include<cstdlib>
12 #include<cstring>
13 #define maxn 1000010
14 #define ll long long
15 struct arr{
16     int l,r,sz,rt;
17 }a[maxn];
18 int l[maxn],r[maxn],d[maxn],n,cnt;
19 ll b[maxn];
20 void swap(int &x,int &y){
21     int tmp=x;x=y;y=tmp;
22 }
23 int merge(int x,int y){
24     if (x==0||y==0) return x+y;
25     if (b[y]>b[x]) swap(x,y);
26     r[x]=merge(r[x],y);
27     if (d[l[x]]<d[r[x]]) swap(l[x],r[x]);
28     d[x]=d[r[x]]+1;
29     return x;//然而啊这道题又是因为这一句没加调了1h
30 }
31 void mer(int x,int y){
32     int lx=a[x].l,rx=a[y].r,size=a[x].sz+a[y].sz,rt=merge(a[x].rt,a[y].rt);
33     a[x].l=lx;a[x].r=rx;a[x].sz=size;a[x].rt=rt;cnt--;
34     while (a[x].sz*2>(a[x].r-a[x].l+2)){
35         a[x].rt=merge(l[a[x].rt],r[a[x].rt]);a[x].sz--;
36     }
37 }
38 int main(){
39     scanf("%d",&n);d[0]=-1;
40     for (int i=1;i<=n;i++) scanf("%d",&b[i]),b[i]-=i;
41     cnt=0;
42     for (int i=1;i<=n;i++){
43         a[++cnt].l=i;a[cnt].r=i;a[cnt].sz=1;a[cnt].rt=i;
44         while (cnt>1&&b[a[cnt].rt]<b[a[cnt-1].rt]) mer(cnt-1,cnt);
45     }
46     ll ans=0;
47     for (int i=1;i<=cnt;i++)
48         for (int j=a[i].l;j<=a[i].r;j++) ans+=abs(b[a[i].rt]-b[j]);
49     printf("%lld\n",ans);
50     return 0;
51 }

  

时间: 2024-10-01 11:59:41

[BZOJ2809&1455&1367]解题报告|可并堆的相关文章

openjudge 9277 堆木头的解题报告

openjudge 9277 堆木头的解题报告.DP-前缀和-优化.(重点).总时间限制: 1000ms 内存限制: 131072kB描述Daxinganling produces a lot of timber. Before loading onto trains, the timberjacks will place the logs to some place in the open air first. Looking from the sideway, the figure of a

2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告

2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告 勘误1:第6题第4个 if最后一个条件粗心写错了,答案应为1580. 条件应为abs(a[3]-a[7])!=1,宝宝心理苦啊.!感谢zzh童鞋的提醒. 勘误2:第7题在推断连通的时候条件写错了,后两个if条件中是应该是<=12 落了一个等于号.正确答案应为116. 1.煤球数目 有一堆煤球.堆成三角棱锥形.详细: 第一层放1个, 第二层3个(排列成三角形), 第三层6个(排列成三角形), 第四层10个(排列成三角形). -. 假设一共

uva 10881 Piotr&#39;s Ants 解题报告

题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=20&page=show_problem&problem=1822 题目意思:有一条 L 厘米长的杆,上面有 n 只蚂蚁,给出每只蚂蚁的朝向以及离杆上最左端的距离,问 T 秒之后每只蚂蚁到达的位置,如果 T 秒后某个位置有多只蚂蚁同时到达,那么这堆蚂蚁处于的位置 + Turning,如果超过这条杆的长度,输出F

hdu 1514 Free Candies 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1514 题目意思:有4堆糖果,每堆糖果有n个,从上到下排好,取糖果只能从上往下取,取完的糖果放在篮子里,篮子里最多放5个,如果篮子里有两个颜色相同的糖果则可以取走放进口袋里,问最多能取走多少对糖果放进口袋.n<=40, 糖果颜色最多20种. 这题是抄这个人滴:http://fudq.blog.163.com/blog/static/1913502382014239225290/ 有些地方看得不太懂,本

常州培训 day3 解题报告

第一题: 给出数轴正半轴上N个点的坐标和其权值,给出初始体力值M,人一开始在位置0,体力值会随着走过路程的增加而增加,走多少个单位的路消耗多少体力值.到每个点可以打掉,消耗的体力值就是其权值.求 最多能打掉多少点. N<=10000,其他<=10^18; 解题过程: 1.一开始就直接想到是贪心,首先人是不可能往左走的,否则不会最优.枚举最后停在哪个点,减去到这个点需要的体力,然后把这个点之前的所有点(包括这个店)的权值排个序,从小到大一个一个打,打到体力没有为止.复杂度分析:枚举N次,每次快拍

济南-1029试题解题报告

By shenben 本解题报告解析均为100分解题思路. 上午 T1 题意:给你两个日期,问这两个日期差了多少毫秒 解析:标程用ctime库函数写的,编译器版本高了才会过(NOIP编译器版本不会太高): 只能老老实实打模拟. 小技巧:最后多输出“000”,计算秒就好了.(特判两个时间相同,只输出“0”) T2 题意:有m个站口,有n个人按顺序排队,求n+1个人最少等多长时间 解析:(标程显然 同下) ①n<m 输出0(显然) ②n>=m 建一个小根堆(可以用优先队列),把前m个人的时间扔到堆

JLOI2015 解题报告

那个嘛= =,虽说是JLOI的解题报告但还差第一题没写= =,就这样行啦 T2:[JLOI2015]城池攻占 首先这道题我们先考虑暴力,也就是每个点向父亲跑,我们考虑能否一起做,可以发现在同一个点的骑士可以用一个堆维护一起跳(因为没有改变优先级的操作)然后再用懒标记维护,我们可以直接用一个可合并堆来维护就可以啦 当然这道题用线段树,倍增都是可行的,就是空间比较拙计罢了,需要用一些奇奇怪怪的方法来干 CODE: 1 #include<cstdio> 2 #include<iostream&

均分纸牌(NOIP2000senior)解题报告

均分纸牌(NOIP2000senior) 解题报告 [题目描述] 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张,但纸牌总数必为 N 的倍数.可以在任一堆上取若于张纸牌,然后移动. 移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上:在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上:其他堆上取的纸牌,可以移到相邻左边或右边的堆上. 现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多. 例如 N=4,4 堆纸牌数分别为: ① 9 ② 8 ③

暑假第二次考试 冲刺Noip2017模拟赛2 解题报告——五十岚芒果酱

题1 牛跑步(running) [题目描述] 新牛到部队,CG 要求它们每天早上搞晨跑,从 A 农场跑到 B 农场.从 A 农场到 B 农场中有 n-2 个路口,分别标上号,A 农场为 1 号,B 农场为 n 号,路口分别为 2...n-1 号,从 A 农场到 B 农场有很多条路径可以到达,而 CG 发现有的路口是必须经过的,即每条路径都经过的路口,CG 要把它们记录下来,这样 CG 就可以先到那个路口,观察新牛们有没有偷懒,而你的任务就是找出所有必经路口. [输入格式] 第一行两个用空格隔开的