UOJ Logo zhouzixuan的博客

博客

bzoj 2164: 采矿

2016-02-13 17:55:23 By zhouzixuan
【题目描述】:
    浩浩荡荡的cg大军发现了一座矿产资源极其丰富的城市,他们打算在这座城市实施新的采矿战略。
  这个城市可以看成一棵有n个节点的有根树,我们把每个节点用1到n的整数编号。为了方便起见,对于任何一个非根节点v,它任何一个祖先的编号都严格小于v。树上的每个节点表示一个矿点,每条边表示一条街道。
  作为cg大军的一个小队长,你拥有m个部下。你有一张二维的动态信息表,用Ti,j表示第i行第j列的数据。当你被允许开采某个区域时,你可以将你的部下分配至各个矿点。在第i个矿点安排j个人可以获得Ti,j单位的矿产。
  允许开采的区域是这样描述的:给你一对矿点(u,v),保证v是u的祖先(这里定义祖先包括u本身);u为你控制的区域,可以在以u为根的子树上任意分配部下;u到v的简单路径(不包括u但包括v,若u=v则包括u)为探险路径,在该路径上你可以选择至多一个矿点安排部下。你这次开采的收益为安排有部下的矿点的收益之和。
【题目解法】:
    不建议在bzoj上看题目,排版简直...TAT

http://www.tsinsen.com/ViewGProblem.page?gpid=A1219

    按照dfs序进行树链剖分,这样既可以维护子树信息,又可以维护链上信息
    令f[i]表示设置任意数量的矿点时放置i个人的最大价值
    令g[i]表示设置一个矿点是放置i个人的最大价值
    将f,g放在线段树每一个结点中
    合并的时候:
    对于f:now.f[i]=max(lc.f[j]+rc.f[i-j])
    对于g:now.g[i]=max(lc.g[i],rc.g[i])
    修改的复杂度为O(C*m^2 log n)
    查询的复杂度为O(C*m log^2 n+C*m)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=20005;
const int M=55;
const int X=1<<16;
const int Y=(1LL<<31)-1LL;
struct edge {int x,next;} b[N];
int n,people,m,L[N],R[N],A,B,Q,tot,a[N];
int top[N],size[N],son[N],dep[N],fa[N]; long long ans;
struct node
{
    long long f[M],g[M];
    inline void clear()
    {
        for (int i=0; i<=people; i++) f[i]=g[i]=0;
    }
} tree[N*4];
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
inline void addedge(int x,int y)
{
    ++tot;
    b[tot].x=y;
    b[tot].next=a[x];
    a[x]=tot;
}
void init()
{
    n=getint(); people=getint(); A=getint(); B=getint(); Q=getint(); dep[1]=1;
    for (int i=2; i<=n; i++) fa[i]=getint(),addedge(fa[i],i),dep[i]=dep[fa[i]]+1; tot=0;
}
void dfs(int x)
{
    size[x]=1; int k=0;
    for (int p=a[x];p;p=b[p].next)
    {
        int pp=b[p].x; dfs(pp); size[x]+=size[pp];
        if (size[pp]>k) k=size[pp],son[x]=pp;
    }
}
void DFS(int x,int anc)
{
    L[x]=++tot; top[x]=anc; if (son[x]!=0) DFS(son[x],anc);
    for (int p=a[x];p;p=b[p].next) if (b[p].x!=son[x]) DFS(b[p].x,b[p].x);
    R[x]=tot;
}
namespace Graph
{
    inline int GetInt()
    {
        /*
        A←((A xor B)+(B div X)+(B * X))and Y
        B←((A xor B)+(A div X)+(A * X))and Y
        返回(A xor B)mod Q
        */
        A=((A^B)+(B/X)+(B*X))&Y;
        B=((A^B)+(A/X)+(A*X))&Y;
        return (A^B)%Q;
    }
    inline void MakeGraph(long long a[])
    {
        for (int i=1; i<=people; i++) a[i]=GetInt();
        sort(a+1,a+people+1); a[0]=0;
        //for (int i=1; i<=people; i++) printf("%d ",a[i]);
        //printf("\n");
    }
}
inline node updata(const node &lc,const node &rc)
{
    node now; now.clear();
    for (int i=1; i<=people; i++) for (int j=0; j<=i; j++) now.f[i]=max(now.f[i],lc.f[j]+rc.f[i-j]);
    for (int i=1; i<=people; i++) now.g[i]=max(lc.g[i],rc.g[i]);
    return now;
}
void modify(int p,int l,int r,int x)
{
    if (l==r)
    {
        Graph::MakeGraph(tree[p].f);
        for (int i=0; i<=people; i++) tree[p].g[i]=tree[p].f[i];
        return;
    }
    int mid=(l+r)/2;
    if (x<=mid) modify(p*2,l,mid,x); else modify(p*2+1,mid+1,r,x);
    tree[p]=updata(tree[p*2],tree[p*2+1]);
}
void build()
{
    for (int i=1; i<=n; i++) modify(1,1,n,L[i]);
}
node ask(int p,int l,int r,int ll,int rr)
{
    if ((l==ll)&&(r==rr)) return tree[p]; int mid=(l+r)/2;
    if (rr<=mid) return ask(p*2,l,mid,ll,rr);
    else if (ll>mid) return ask(p*2+1,mid+1,r,ll,rr);
    else return updata(ask(p*2,l,mid,ll,mid),ask(p*2+1,mid+1,r,mid+1,rr));
}
node ask(int x,int y)
{
    node now; now.clear();
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        now=updata(now,ask(1,1,n,L[top[x]],L[x]));
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    now=updata(now,ask(1,1,n,L[x],L[y]));
    return now;
}
void Ask(int x,int y)
{
    node Tree=ask(1,1,n,L[x],R[x]); ans=0;
    node Link; Link.clear(); if (x!=y) Link=ask(fa[x],y);
    for (int i=0; i<=people; i++) ans=max(ans,Tree.f[i]+Link.g[people-i]);
    printf("%lld\n",ans);
}
void solve()
{
    m=getint();
    for (int i=1; i<=m; i++)
    {
        int opt=getint();
        if (opt==0) {int x=getint(); modify(1,1,n,L[x]);}
        if (opt==1) {int x=getint(),y=getint(); Ask(x,y);}
    }
}
int main()
{
    init();
    dfs(1);
    DFS(1,1);
    build();
    solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。