【NOIP模拟】树塔狂想曲

Description

相信大家都在长训班学过树塔问题,题目很简单求最大化一个三角形数塔从上往下走的路径和。走的规则是:(i,j)号点只能走向(i+1,j)或者(i+1,j+1)。如下图是一个数塔,映射到该数塔上行走的规则为:从左上角的点开始,向下走或向右下走直到最底层结束。

   1
   3 8
   2 5 0
   1 4 3 8
   1 4 2 5 0

   路径最大和是1+8+5+4+4 = 22,1+8+5+3+5 = 22或者1+8+0+8+5 = 22。

   小S觉得这个问题so easy。于是他提高了点难度,他每次ban掉一个点(即规定哪个点不能经过),然后询问你不走该点的最大路径和。
   当然他上一个询问被ban掉的点过一个询问会恢复(即每次他在原图的基础上ban掉一个点,而不是永久化的修改)。

Solution

看起来很简单,实际上也很简单。
用f[i][j]表示顶点到这个点的最长路,g[i][j]表示这个点到底层的最短路。
如果一个点被ban了,那么要让这个点不受影响,那么只能用这一层的点的f[i][j]+g[i][j]-a[i][j]的max值,同层的点实绝对不会经过的。
其实 n2 预处理就可以了。但是我用了线段树,能过就行了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=1007;
int i,j,k,l,n,m,ans,x,y,ans1,ans2;
int a[maxn][maxn],f[maxn][maxn],g[maxn][maxn];
int t[500007*4],b[1000007];
void build(int x,int l,int r){
    if(l==r){
        t[x]=b[l];
        return;
    }
    int mid=(l+r)/2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
    t[x]=max(t[x*2],t[x*2+1]);
}
int find(int x,int l,int r,int y,int z){
    if(y>z)return 0;
    if(l==y&&r==z){
        return t[x];
    }
    int mid=(l+r)/2;
    if(z<=mid)return find(x*2,l,mid,y,z);
    else if(y>mid)return find(x*2+1,mid+1,r,y,z);
    else{
        return max(find(x*2,l,mid,y,mid),find(x*2+1,mid+1,r,mid+1,z));
    }
}
int read(){
    int x=0;
    char ch=getchar();
    while(ch>'9'||ch<'0')ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=getchar();
    return x;
} 
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    n=read(),m=read();
    fo(i,1,n){
        fo(j,1,i){
            a[i][j]=read();
        }
    }
    fo(i,1,n){
        fo(j,1,i){
            f[i][j]=max(f[i][j],f[i-1][j-1]+a[i][j]);
            f[i][j]=max(f[i][j],f[i-1][j]+a[i][j]);
        }
    }
    fo(i,1,n)g[n][i]=a[n][i],ans1=max(ans1,f[n][i]);
    fod(i,n-1,1){
        fo(j,1,i){
            g[i][j]=max(g[i][j],g[i+1][j+1]+a[i][j]);
            g[i][j]=max(g[i][j],g[i+1][j]+a[i][j]);
        }
    }
    fo(i,1,n)fo(j,1,i)b[++b[0]]=f[i][j]+g[i][j]-a[i][j];
    build(1,1,b[0]);
    while(m--){
        x=read(),y=read();
        if(x==1&&y==1)printf("-1\n");
        else{
            ans=0;
            int u=(1+(x-1))*(x-1)/2+1,v=(1+x)*x/2,now=u-1+y;
            ans=max(ans,find(1,1,b[0],u,now-1));
            ans=max(ans,find(1,1,b[0],now+1,v));
            printf("%d\n",ans);
        }
    }
}

你可能感兴趣的:(noip,DP,线段树)