BZOJ 4458: GTY的OJ【贪心】【倍增】【超级钢琴】

Description

身为IOI金牌的gtyzs有自己的一个OJ,名曰GOJ。GOJ上的题目可谓是高质量而又经典,他在他的OJ里面定义了一个树形的分类目录,且两个相同级别的目录是不会重叠的。比如图论的大目录下可能分为最短路,最小生成树,网络流等低一级的分类目录,这些目录下可能还有更低一级的目录,以此类推。现在gtyzs接到了一个任务,要他为SDTSC出题。他准备在自己的OJ题库中找出M道题作为SDTSC的试题,而他深知SDTSC的童鞋们个个都是神犇,所以gtyzs认为自己出的这M道题中,每道题都应该属于不少于L种分类目录;可他又怕自己的题没有人会做,所以每道题也应该属于不超过R种分类目录,并且这些分类目录应该是连续的,不能出现断层。对了,最重要的是,不存在一道题,它属于两个分类目录且这两个目录不是包含关系。(比如不存在一道题它既是一道DP题,又是一道网络流题)gtyzs怕被骂,所以他希望不存在任意的一对题目(u,v),使得u所属的分类目录与v完全相同。举例来说,gtyzs不能出两道同样的都是动态规划,背包的题,但是却可以出一道属于动态规划,背包和01背包的题和一道属于背包,01背包的题,当然也可以出一个属于图论的题和一个属于数论的题。(注意:一道题所属的分类目录不一定从根开始,如加粗部分所示)
为了让自己的题目变得更加靠谱,他给每一个分类目录都定了一个靠谱值ai,这个值可正可负。一道题的靠谱度为其从属的分类目录靠谱值的加和。我们假设动态规划的靠谱值为10,插头DP的靠谱值为-5,则一道动态规划插头DP的题的靠谱值就是5。gtyzs希望自己出的这M道题,在满足上述前提条件下,靠谱度总和最大。gtyzs当然会做啦,于是你就看到了这个题。

题解

类似于超级钢琴,只不过是在树上的。只要把RMQ做到树上就可以了,但是有很多细节,写的时候需要仔细。

代码

#include
#include
#include
#include
#include
#define maxn 500006
#define LL long long
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0,p=1;
    while(ch!='-'&&!(ch>='0'&&ch<='9'))ch=nc();
    if(ch=='-')p=-1,ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum*p;
}
struct data{
    int s,l,r,t,sum;
    bool operator <(const data&b)const{return sum heap;
int n,T,L,R,tot,root,a[maxn],dep[maxn],lnk[maxn],son[maxn],nxt[maxn],fa[maxn][20],f[maxn][20],g[maxn][20];
LL ans;
void add(int x,int y){
    nxt[++tot]=lnk[x];son[tot]=y;lnk[x]=tot;
}
void dfs(int x){
    for(int j=lnk[x];j;j=nxt[j]){
        a[son[j]]+=a[x];dep[son[j]]=dep[x]+1;
        dfs(son[j]);
    }
}
void make_p(){
    for(int j=1;j<=log2(n);j++)
     for(int i=1;i<=n;i++){
        fa[i][j]=fa[fa[i][j-1]][j-1];
        if(f[i][j-1]1]][j-1])f[i][j]=f[i][j-1],g[i][j]=g[i][j-1];
                                   else f[i][j]=f[fa[i][j-1]][j-1],g[i][j]=g[fa[i][j-1]][j-1];
     }
}
int get(int x,int l,int r){
    int xx=x;
    for(int j=19;j>=0;j--) if(dep[x]-(1<=dep[xx]-l)x=fa[x][j];
    int sumx=1e9,sumy;
    for(int j=19;j>=0;j--) if(dep[x]-(1<=dep[xx]-r){
        if(f[x][j]if(f[x][0]0],sumy=g[x][0];
    return sumy;
}
int main(){
    freopen("h.in","r",stdin);
    freopen("h.out","w",stdout);
    n=_read();
    for(int i=1;i<=n;i++){
        fa[i][0]=_read();
        if(!fa[i][0])root=i;
        add(fa[i][0],i);
    }
    for(int i=1;i<=n;i++)a[i]=_read();dep[0]=1;dep[root]=2;fa[root][0]=0;
    dfs(root);
    for(int i=1;i<=n;i++)f[i][0]=a[i],g[i][0]=i;
    T=_read();L=_read();R=_read();
    make_p();
    for(int i=1;i<=n;i++) if(dep[i]>L){
        data p;
        p.s=i;p.l=L;p.r=min(dep[i]-1,R);
        p.t=get(i,p.l,p.r);p.sum=a[i]-a[p.t];
        heap.push(p);
    }
    while(T--){
        data p=heap.top(),p1,p2;heap.pop();
        ans+=p.sum;
        p1.s=p2.s=p.s;
        p1.l=p.l;p1.r=dep[p.s]-dep[p.t]-1;
        p2.l=dep[p.s]-dep[p.t]+1;p2.r=p.r;
        if(p1.l<=p1.r)p1.t=get(p1.s,p1.l,p1.r),p1.sum=a[p1.s]-a[p1.t],heap.push(p1);
        if(p2.l<=p2.r)p2.t=get(p2.s,p2.l,p2.r),p2.sum=a[p2.s]-a[p2.t],heap.push(p2);
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(BZOJ,倍增,贪心,超级钢琴)