• [APIO2012]


    来自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 = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    
    int n,dfn=0,cnt=0,mx[MN+5],size[MN+5],nl[MN+5],nr[MN+5],head[MN+5],id[MN+5],rt[MN+5];
    ll c[MN+5],l[MN+5],ans=0,m;
    struct edge{
        int to,next;
    }e[MN+5];
    struct TREE{
        int l,r,size;ll x;
    }T[MM+5];
    
    void ins(int f,int t)
    {
        e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    }
    
    void dfs1(int x)
    {
        size[x]=1;mx[x]=0;int maxn=0;
        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+1;T[nx].x=T[x].x+c;
        ll l=1,r=INF,mid;
        while(l<r)
        {
            mid=l+r>>1;
            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+1;nx=T[nx].r;x=T[x].r;
            }
            T[nx].size=T[x].size+1;T[nx].x=T[x].x+c;
        }
    }
    
    int query(int x,int nx,ll cc)
    {
        ll l=1,r=INF,mid,num=0;
        while(l<r)
        {
            mid=l+r>>1;
            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+1;
            }
            else
            {
                x=T[x].l;nx=T[nx].l;
                r=mid;
            }
        }
        if(T[nx].size-T[x].size>0)
        {
            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=1;i<=n;i++)
        {
            int fa=read();c[i]=read();l[i]=read();
            if(fa) ins(fa,i);
        }
        dfs1(1);dfs2(1);cnt=0;
        for(int i=1;i<=n;i++)
            ins(rt[i-1],rt[i]=++cnt,c[id[i]]);
        for(int i=1;i<=n;i++)
            ans=max(ans,l[i]*1LL*query(rt[nl[i]-1],rt[nr[i]],m));
        cout<<ans;
        return 0;
    }

    B.guard守卫

    有n个草丛(#滑稽),里面有K个盖伦忍者,有m个条信息,表示一段区间内有没有盖伦忍者,你要求出一定有盖伦忍者的草丛。   $n,m,kleqslant 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 = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    struct TREE{int l,r,val,x;}T[MN*4+5];
    struct ques{int l,r;
        bool operator <(const ques&y)const{return l==y.l?r>y.r:l<y.l;}
    }q[MN+5],s[MN+5];
    int n,K,m,cnt=0,f[MN+5],g[MN+5],nl[MN+5],nr[MN+5],cn=0,id[MN+5];
    
    void build(int x,int l,int r)
    {
        if((T[x].l=l)==(T[x].r=r)) {T[x].x=1;return;}
        int mid=l+r>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
        T[x].x=T[x<<1].x+T[x<<1|1].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=1;T[x].x=0;
            return;
        }
        int mid=T[x].l+T[x].r>>1;
        if(r<=mid) modify(x<<1,l,r);
        else if(l>mid) modify(x<<1|1,l,r);
        else {modify(x<<1,l,mid);modify(x<<1|1,mid+1,r);}
        T[x].x=T[x<<1].x+T[x<<1|1].x;
    }
    
    int query(int x,int k)
    {
        if(T[x].val)return 0;
        if(T[x].l==T[x].r)return T[x].x;
        int mid=T[x].l+T[x].r>>1;
        return k<=mid?query(x<<1,k):query(x<<1|1,k);
    }
    
    int get(int x,int l,int r)
    {
        int ans=0;
        for(int mid=l+r>>1;l<=r;mid=l+r>>1)
            if(s[mid].r<x) ans=mid,l=mid+1;
            else r=mid-1;
        return f[ans];
    }
    
    int get2(int x,int l,int r)
    {
        int ans=0;
        for(int mid=l+r>>1;l<=r;mid=l+r>>1)
            if(s[mid].l>x) ans=mid,r=mid-1;
            else l=mid+1;
        return g[ans];
    }
    
    int main()
    {
        n=read();K=read();m=read();
        build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            int l=read(),r=read(),x=read();
            if(!x) modify(1,l,r);
            else q[++cnt]=(ques){l,r};
        }
        if(T[1].x==K)
        {
            for(int i=1;i<=n;i++)
                if(query(1,i)==1)
                    printf("%d
    ",i);
            return 0;
        }
        for(int i=1;i<=n;i++)
            if(query(1,i)==1)
                nl[i]=++cn,id[cn]=i;
        for(int i=n;i;i--)
            nr[i]=(nl[i]?nl[i]:nr[i+1]);
        for(int i=1;i<=n;i++)
            nl[i]=(nl[i]?nl[i]:nl[i-1]);
        for(int i=1;i<=cnt;i++)
            q[i].l=nr[q[i].l],q[i].r=nl[q[i].r];
        sort(q+1,q+cnt+1);int j=0;
        for(int i=1;i<=cnt;i++)
        {
            while(j&&q[i].r<=s[j].r)--j;
            s[++j]=q[i];
        }
        for(int i=1,mx=0;i<=j;i++)
            if(s[i].l>mx) {mx=s[i].r;f[i]=f[i-1]+1;}
            else f[i]=f[i-1];
        for(int k=j,mn=n+1;k;k--)
            if(s[k].r<mn) {mn=s[k].l;g[k]=g[k+1]+1;}
            else g[k]=g[k+1];
        bool flag=0;
        for(int i=1;i<=j;i++)
        {
            if(f[i]!=f[i-1]+1) continue;
            if(s[i].l==s[i].r) {flag=1;printf("%d
    ",id[s[i].l]);continue;}
            int lt=get(s[i].r-1,1,i-1),rt=get2(s[i].r-1,i+1,j);
            if(lt+rt+1>K) printf("%d
    ",id[s[i].r]),flag=1;
        }
        if(!flag) puts("-1");
        return 0;
    }

    C.苦无

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

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

  • 相关阅读:
    008Spring & JPA & Hibernate & MySQL
    007Spring Security
    006Spring面向切面
    005运行时值注入
    004bean作用域
    003自动装配歧义性解决
    002Conditional条件化创建bean
    001profile条件化创建bean
    007API网关服务Zuul
    Sqlserver2008R2 如何使用bak备份文件还原数据库以及对应的失败解决方案
  • 原文地址:https://www.cnblogs.com/FallDream/p/apio2012.html
Copyright © 2020-2023  润新知