【BZOJ 2809】dispatching(主席树)

这道题用主席树做做感觉非常舒服~~~

首先题意来看,是说需要在树形结构中找到一个点i,并且找到这个点子树中的一些点组成一个集合,使得集合中的c之和不超过M,且Li*集合中元素个数和最大

简单地想想首先需要枚举每一个点,然后在子树中找到最小的k个点,使得sigma(C[i])(i = 1..k)不超过M,那么L[i]*k就是对于这个点来说的最优解

那么我们应该想到可以利用主席树中的性质,首先将树形结构通过dfs序转化到线性结构上,然后可以通过对子树中的记录信息进行判断从而得到答案(详见代码)

再想想其实有些点没有必要对它进行枚举。如果它的祖先中有L比它大,那么这个点肯定不会优于它的祖先,因此不用枚举它了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define Rep(a,b,c) for(int a = b ; a <= c ; a ++)
#define clr(a) memset(a,0,sizeof(a))
#define clr1(a) memset(a,-1,sizeof(a))
#define LL long long
#define Mid int mid = (l + r) >> 1
#define N 100500
using namespace std;
struct edge{
    int to,next;
};
struct node{
    node *ch[2];
    LL w;
    int siz;
    void init(){
        w = siz = 0;
        ch[0] = ch[1] = NULL;
    }
    node(){init();}
    void update(){
        siz = w = 0;
        if(ch[0]) {siz += ch[0]->siz;w += ch[0]->w;}
        if(ch[1]) {siz += ch[1]->siz;w += ch[1]->w;}
    }
}T[N * 20];
int qcnt,size;
int C[N],L[N],C2[N];
struct SegmentTree{
    node *root[N],*null;
    void init(){
        null = &T[qcnt ++];
        null->init();
        null->ch[0] = null->ch[1] = null;
    }
    void __build(node *&y,node *&x,int l,int r,int t){
        if(x == NULL) x = null;
        y = &T[qcnt ++];
        y->init();
        if(l == r){
            *y = *x;
            y->w += t;
            y->siz ++;
            return;
        }
        Mid;
        if(t <= C2[mid]){
            __build(y->ch[0],x->ch[0],l,mid,t);
            y->ch[1] = x->ch[1];
            y->update();
        }
        else{
            __build(y->ch[1],x->ch[1],mid+1,r,t);
            y->ch[0] = x->ch[0];
            y->update();
        }
    }
    void __find(node *x1,node *x2,int l,int r,int lim,int &ans){
        if(x1 == NULL) x1 = null;
        if(x2 == NULL) x2 = null;
        if(l == r){
            ans += min(lim / C2[l],x2->siz - x1->siz);
            return;
        }
        LL lw = 0;
        int ls = 0;
        lw += x2->ch[0]->w;ls += x2->ch[0]->siz;
        lw -= x1->ch[0]->w;ls -= x1->ch[0]->siz;
        Mid;
        if(lim >= lw){
            ans += ls;
            __find(x1->ch[1],x2->ch[1],mid + 1,r,lim - lw,ans);
        }
        else __find(x1->ch[0],x2->ch[0],l,mid,lim,ans);
    }
    void build(int id,int val){
        __build(root[id],root[id - 1],1,size,val);
    }
    int find(int x,int y,int lim){
        int ans = 0;
        __find(root[x - 1],root[y],1,size,lim,ans);
        return ans;
    }
}sgt;
edge e[N << 1];
int head[N],sz;
int l[N],r[N],dfsc,rank[N];
bool needs[N];
int n,m;
void addedge(int u,int v){
    e[sz].to = v;
    e[sz].next = head[u];
    head[u] = sz ++;
}
void init(){
    clr(needs);
    clr1(head);
    sz = dfsc = qcnt = 0;
}
void dfs(int u,int Max){
    l[u] = ++dfsc;
    rank[dfsc] = u;
    if(Max < L[u]) needs[u] = 1;
    int nmax = max(Max,L[u]);
    for(int i = head[u] ; i != -1 ; i = e[i].next){
        int v = e[i].to;
        dfs(v,nmax);
    }
    r[u] = dfsc;
}
void solve(){
    int b;
    init();
    sgt.init();
    Rep(i,1,n){
        scanf("%d",&b);
        addedge(b,i);
        scanf("%d%d",&C[i],&L[i]);
        C2[i] = C[i];
    }
    dfs(0,-1);
    sort(C2 + 1,C2 + n + 1);
    size = unique(C2 + 1,C2 + n + 1) - (C2 + 1);
    Rep(i,1,dfsc) sgt.build(i,C[rank[i]]);
    LL ans = -1;
    Rep(i,1,n){
        if(!needs[i]) continue;
        ans = max(ans,(LL)sgt.find(l[i],r[i],m) * L[i]);
    }
    printf("%lld\n",ans);
}
int main()
{
    while(~scanf("%d%d",&n,&m)) solve();
    return 0;
}
时间: 2024-10-27 05:54:42

【BZOJ 2809】dispatching(主席树)的相关文章

BZOJ 2809: [Apio2012]dispatching [主席树 DFS序]

传送门 题意:查询树上根节点值*子树中权值和$\le m$的最大数量 最大值是多少 求$DFS$序,然后变成区间中和$\le m$最多有几个元素,建主席树,然后权值线段树上二分就行了 $WA$:又把边表开小了..... 好吧我$zz$了有根树加无向边干什么.... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #de

bzoj 2653 middle (主席树+二分)

版权声明:本文为博主原创文章,未经博主允许不得转载. bzoj 2653 题意: 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整. 给你一个长度为n的序列s. 回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数. 其中a<b<c<d. 位置也从0开始标号. 强制在线. 解法: 首先可以想到的是二分答案,再判断是否满足条件 . 对于答案x,我们将原数组中大于等于x的数记1,小于的记为-1,

BZOJ 2735: 世博会 主席树+切比雪夫距离转曼哈顿距离

2735: 世博会 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 124  Solved: 51[Submit][Status][Discuss] Description 四年一度的世博会又要举办了,Q国很荣幸成为了这次世博会的主办方.Q国主席QQ从全国各地收集了N件物品排成 一排,作为Q国馆的展出物.对于相邻摆放的一些物品,如果过于相似会让人觉得无聊,如果差别过大又会让人觉 得突兀.为了让人们对这次世博会的展出满意,QQ需要知道一些相邻物品的“

bzoj 3653 谈笑风生——主席树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3653 原来一直想怎么线段树合并.可是不会把角标挪一位. 查询的其实是子树内一段深度的点的 siz 和.因为是子树内,所以按 dfs 序建立主席树,角标是 dep ,值是 siz . 注意 long long .而且数组 *19 就会 RE ,*20就好了.不知为何. #include<iostream> #include<cstdio> #include<cstring

BZOJ 2653: middle [主席树 中位数]

传送门 题意: 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整.给你一个 长度为n的序列s.回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数. 我会使用一些方式强制你在线. 最后一句话太可怕了$QAQ$ 首先需要知道怎么求中位数: 二分答案,$\ge$的为$1$,$<$的为$-1$,如果和$\ge 0$说明当前答案$\le$中位数 最大中位数?$GSS$! 只要求$[a,b].rm+(b,c)

BZOJ 2809 dispatching

APIO题...我是拿左偏树做的. #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define maxv 100500 #define maxe 100500 using namespace std; long long n,m,tree[maxv][3],fath[maxv],g[maxv],nume=0,size[maxv],val[maxv],dis[max

BZOJ 3514 LCT+主席树

思路: //By SiriusRen #include <bits/stdc++.h> using namespace std; const int N=400500; int n,m,k,type,ch[N][2],fa[N],minn[N],rev[N],q[N],top,pre[N]; int root[N],tree[N*50],lson[N*50],rson[N*50],cnt,xx,yy,ans; struct Road{int x,y,wei;}road[N]; bool isr

BZOJ 2653 middle | 主席树

题目: http://www.lydsy.com/JudgeOnline/problem.php?id=2653 题解: 设答案为ans,把大于等于ans的记为1,小于的记为-1,这样可以知道当前ans是大了还是小了 然后二分答案,就是求最大子段和的问题,根据网上的题解:[b,c]是必选的,然后选[a,b]和[c,d]的最大字段和就行了 #include<cstdio> #include<cstring> #include<algorithm> #define N 20

AC日记——dispatching bzoj 2809

2809: [Apio2012]dispatching Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3290  Solved: 1740[Submit][Status][Discuss] Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是