bzoj 2164 采矿

http://www.elijahqi.win/archives/3700
Description
浩浩荡荡的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)为探险路径,在该路径上你可以选择至多一个矿点安排部下。你这次开采的收益为安排有部下的矿点的收益之和。

Input
输入的第一行包含5个正整数n、m、A、B、Q。n为矿点的个数,m为部下的数量。A、B、Q是与动态信息表有关的数据。第二行包含n-1个正整数,第i个数为Fi+1,表示节点i+1的父亲。接下来需要你用下文的方法依次生成n组数据,每组数据共m个。其中第i组的m个数据为信息表中第i行的m个数据。紧接着一行包含一个正整数C,表示事件的数量。最后给出C行,每行描述一个事件。每个事件会先给出一个0或1的整数。如果该数为0,则后面有一个正整数p,表示动态信息表有更新,你需要生成一组m个数据,来替换信息表中第p行的m个数据。如果该数为1,则后面有两个正整数u、v,表示出现了一个你可以开采的区域,你需要回答这次开采的收益。同一行的各个数之间均用一个空格隔开,没有多余的空格和换行。数据的生成方法如下:每次生成一组m个从小到大排列的数据,替换动态信息表的一行。其中,从小到大第j个数替换信息表中第j列的数。调用以下代码m次并排序得到一组数据。(注意可能会出现重复的数)函数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、B、Q均用32位有符号整数保存(C/C++的signed long int类型,pascal的longint类型),X=216(2的16次方),Y=231-1(2的31次方-1),xor为位异或运算,div为整除运算,and为位且运算,mod为取余运算。由于只保留了低31位,易得我们不用考虑数据的溢出问题。(注意每次A和B都会被改变)

Output
对于每个开采事件(开头为1的事件),输出一行一个整数,为每次的收益。

Sample Input
10 5 1 2 10
1 1 3 3 4 4 6 6 9
4
1 6 3
1 9 1
0 1
1 1 1
Sample Output
11
9
12
【样例说明】
最初的信息表如下
1 2 3 4 5
1 0 1 1 2 2
2 0 5 7 7 9
3 1 2 3 4 5
4 0 1 2 4 5
5 2 4 7 8 8
6 0 2 3 8 9
7 1 3 5 6 8
8 3 3 3 7 8
9 0 1 2 3 9
10 0 0 1 4 4
变化后的第1行为
1 1 1 1 4 7
第一次开采可以在矿点6、8、9、10任意安排,可以在矿点3或4中选取一个安排开采。一种最优安排是在矿点6安排4人,在矿点8安排1人。第二次开采可以在矿点9安排,可以在矿点6、4、3、1中选择一个安排。一种最优安排是在矿点9安排1人,在矿点6安排4人。
HINT
有50%的数据,对于满足2≤i≤n的整数i,Fi=i-1。这些数据中有40%的数据(即所有数据的20%)满足n≤500,m≤20,C≤500。除上述数据,另有40%的数据满足n≤500,m≤20,C≤500。对于100%的数据1≤n≤20000,1≤m≤50,1≤C≤2000。对于满足2≤i≤n的整数i,1≤Fi<i。1≤A,B≤231-1,1≤Q≤10000。

暴力朴素dp 应该没有不会的

但是数据太大 一开始没想好 但是确实这个dp其实可以在线段树上来维护

那么就每个节点维护 两个信息 一个是只能在该区间一个节点选的信息 存在ans[]数组

另一个是可以在该区间 若干节点选 然后组合起来 存在ans1[]数组

注意题目的限制 如果有y!=x 应该特殊处理x节点

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=20020,M=55;
int X=1<<16,Y=~0u>>1,A,B,Q,t[N][M],h[N],num,in[N],out[N],size[N],son[N];
int tp[N],fa[N],dep[N],C,id[N],m,n;
struct node{
    int y,next;
}data[N<<1];
inline int getint(){
    A=((A^B)+(B/X)+(B*X))&Y;
    B=((A^B)+(A/X)+(A*X))&Y;
    return (A^B)%Q;
}
inline void insert1(int x,int y){
    data[++num].y=y;data[num].next=h[x];h[x]=num;
    data[++num].y=x;data[num].next=h[y];h[y]=num;
}
inline void dfs(int x){
    size[x]=1;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (y==fa[x]) continue;
        fa[y]=x;dep[y]=dep[x]+1;dfs(y);size[x]+=size[y];
        if (size[y]>size[son[x]]) son[x]=y;
    }
}
inline void dfs1(int x,int top){
    in[x]=++num;tp[x]=top;id[num]=x;
    if (son[x]) dfs1(son[x],top);
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if(y==fa[x]||y==son[x]) continue;
        dfs1(y,y);
    }out[x]=num;
}
ll dp[N<<2][M],dp1[N<<2][M],ans[M],ans1[M];
inline void merge(ll *x,ll *a,ll *b){
    for (int i=1;i<=m;++i) x[i]=max(a[i],b[i]);
}
inline void merge1(ll *x,ll *a,ll *b){
    static ll v[M];memset(v,0,sizeof(v));
    for (int i=0;i<=m;++i) for (int j=0;j<=m-i;++j) v[i+j]=max(v[i+j],a[i]+b[j]);
    for (int i=1;i<=m;++i) x[i]=v[i];
}
inline void build(int x,int l,int r){
    if (l==r) {
        for (int i=1;i<=m;++i) dp[x][i]=dp1[x][i]=t[id[l]][i];return;
    }int mid=l+r>>1;
    build(lc,l,mid);build(rc,mid+1,r);
    merge(dp[x],dp[lc],dp[rc]);merge1(dp1[x],dp1[lc],dp1[rc]);
}
inline void modify(int x,int l,int r,int p){
    if (l==r){
        for (int i=1;i<=m;++i) dp[x][i]=dp1[x][i]=t[id[p]][i];return;
    }int mid=l+r>>1;
    if (p<=mid) modify(lc,l,mid,p);else modify(rc,mid+1,r,p);
    merge(dp[x],dp[lc],dp[rc]);merge1(dp1[x],dp1[lc],dp1[rc]);
}
inline void qr(int x,int l,int r,int l1,int r1){
    if (l1<=l&&r1>=r) {merge(ans,ans,dp[x]);return;}
    int mid=l+r>>1;
    if (l1<=mid) qr(lc,l,mid,l1,r1);
    if (r1>mid) qr(rc,mid+1,r,l1,r1);
}
inline void qr1(int x,int l,int r,int l1,int r1){
    if (l1<=l&&r1>=r) {merge1(ans1,ans1,dp1[x]);return;}
    int mid=l+r>>1;
    if (l1<=mid) qr1(lc,l,mid,l1,r1);
    if (r1>mid) qr1(rc,mid+1,r,l1,r1);
}
int main(){
    freopen("bzoj2164.in","r",stdin);
    n=read();m=read();A=read();B=read();Q=read();
    for (int i=2;i<=n;++i) insert1(read(),i);
    dfs(1);num=0;dfs1(1,1);C=read();
    for (int i=1;i<=n;++i){
        static int v[M];
        for (int j=1;j<=m;++j) v[j]=getint();
        sort(v+1,v+m+1);
        for (int j=1;j<=m;++j) t[i][j]=v[j];
    }build(1,1,n);
    /*for (int i=1;i<=n;++i){
        for (int j=1;j<=m;++j) printf("%d ",t[i][j]);puts("");
    }*/
    for (int owo=1;owo<=C;++owo){
        int op=read();
        if (op==0){
            int p=read();static int v[M];
            for (int j=1;j<=m;++j) v[j]=getint();
            sort(v+1,v+m+1);
            for (int j=1;j<=m;++j) t[p][j]=v[j];modify(1,1,n,in[p]);
        }else{
            int x=read(),y=read();
            memset(ans,0,sizeof(ans));memset(ans1,0,sizeof(ans1));
            qr1(1,1,n,in[x],out[x]);
            if (x==y) {printf("%lld\n",ans1[m]);continue;}x=fa[x];
            while(tp[x]!=tp[y]){
                qr(1,1,n,in[tp[x]],in[x]);x=fa[tp[x]];
            }qr(1,1,n,in[y],in[x]);merge1(ans,ans,ans1);
            printf("%lld\n",ans[m]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值