2164: 采矿|树链剖分|DP

Posted ws_yzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2164: 采矿|树链剖分|DP相关的知识,希望对你有一定的参考价值。

DP比较显然,但是直接DP会Tle,这时需要树链剖分用线段树维护dp值同时维护链上的和子树的dp值

#include<algorithm>
#include<iostream>
#include<complex>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<ctime>
#include<set>
#include<map>
#define X 65536
#define Y 2147483647
#define N 200010
#define M 51
using namespace std;
int sc()

    int i=0,f=1; char c=getchar();
    while(c>'9'||c<'0')if(c=='-')f=-1;c=getchar();;
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i*f;

int n,m,A,B,Q,tot,cnt;
int fa[N],top[N],deep[N],size[N];
int head[N],nxt[N],lst[N];
int m1[N][M],m2[N][M],a[N][M];
int S[N],T[N],wh[N];
int ans1[M],ans2[M];
inline int get_int()

    A=((A^B)+(B/X)+(B*X))&Y;
    B=((A^B)+(A/X)+(A*X))&Y;
    return (A^B)%Q;

void insert(int x,int y)

    lst[++tot]=y;nxt[tot]=head[x];head[x]=tot;

void dfs(int x)

    size[x]=1;
    for(int i=head[x];i;i=nxt[i])
    
        deep[lst[i]]=deep[x]+1;
        dfs(lst[i]);
        size[x]+=size[lst[i]];
    

void dfs(int x,int htp)

    int k=0;top[x]=htp;S[x]=T[x]=++cnt;wh[cnt]=x;
    for(int i=head[x];i;i=nxt[i])
        if(size[lst[i]]>size[k])k=lst[i];
    if(!k)return;dfs(k,htp);
    for(int i=head[x];i;i=nxt[i])
        if(lst[i]!=k)
            dfs(lst[i],lst[i]);
    T[x]=cnt;

void push_up(int x)

    int L=x<<1,R=x<<1|1;
    for(int i=1;i<=m;i++)
    
        m2[x][i]=m1[x][i]=max(m1[L][i],m1[R][i]);
        for(int j=0;j<=i;j++)
            m2[x][i]=max(m2[x][i],m2[L][j]+m2[R][i-j]);
    

void build(int x,int l,int r)

    if(l==r)
    
        for(int i=1;i<=m;i++)m1[x][i]=m2[x][i]=a[wh[l]][i];
        return ;
    
    int mid=l+r>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    push_up(x);

void change(int x,int L,int R,int p)

    if(L==R)
    
        for(int i=1;i<=m;i++)m1[x][i]=m2[x][i]=a[wh[L]][i];
        return ;
    
    int mid=L+R>>1;
    if(p<=mid)change(x<<1,L,mid,p);
    else if(mid<p)change(x<<1|1,mid+1,R,p);
    push_up(x);

void query(int x,int L,int R,int l,int r)

    if(l==L&&r==R)
    
        for(int i=m;i>=1;i--)
            for(int j=0;j<i;j++)
                ans1[i]=max(ans1[i],ans1[j]+m2[x][i-j]);
        return ;
    
    int mid=L+R>>1;
    if(r<=mid)query(x<<1,L,mid,l,r);
    else if(mid<l)query(x<<1|1,mid+1,R,l,r);
    else query(x<<1,L,mid,l,mid),query(x<<1|1,mid+1,R,mid+1,r);

void query0(int x,int L,int R,int l,int r)

    if(l==L&&R==r)
    
        for(int i=1;i<=m;i++)
            ans2[i]=max(ans2[i],m1[x][i]);
        return;
    
    int mid=L+R>>1;
    if(r<=mid)query0(x<<1,L,mid,l,r);
    else if(l>mid)query0(x<<1|1,mid+1,R,l,r);
    else query0(x<<1,L,mid,l,mid),query0(x<<1|1,mid+1,R,mid+1,r);

void solve(int x,int y)

    if(x==y)return;x=fa[x];
    while(top[x]!=top[y])
    
        query0(1,1,n,S[top[x]],S[x]);
        x=fa[top[x]];
    
    query0(1,1,n,S[y],S[x]);

int main()

    //freopen("energy.in","r",stdin);
    //freopen("energy.out","w",stdout);
    n=sc(),m=sc(),A=sc(),B=sc(),Q=sc();
    for(int i=2;i<=n;i++)
        fa[i]=sc(),insert(fa[i],i);
    for(int i=1;i<=n;i++)
    
        for(int j=1;j<=m;j++)
            a[i][j]=get_int();
        sort(a[i]+1,a[i]+m+1);
    
    dfs(1);dfs(1,1);build(1,1,n);
    int W=sc();
    while(W--)
    
        int opt=sc();
        if(opt==1)
        
            int x=sc(),y=sc(),ans=0;
            memset(ans1,0,sizeof(ans1));
            memset(ans2,0,sizeof(ans2));

            query(1,1,n,S[x],T[x]);
            solve(x,y);

            for(int i=0;i<=m;i++)
                ans=max(ans,ans1[i]+ans2[m-i]);
            printf("%d\\n",ans);
        
        else
        
            int x=sc();
            for(int i=1;i<=m;i++)
                a[x][i]=get_int();
            sort(a[x]+1,a[x]+m+1);
            change(1,1,n,S[x]);
        
    
    return 0;

以上是关于2164: 采矿|树链剖分|DP的主要内容,如果未能解决你的问题,请参考以下文章

Bzoj2164 采矿(线段树+树链剖分)

BZOJ2164 : 采矿

树链剖分(轻/重链剖分学习笔记)

树链剖分

模板树链剖分

树链剖分详解