HDU6766 Diamond Rush(可持久化线段树维护DP+线段树哈希)

HDU6766 Diamond Rush

题意:

给出一个矩阵,里面每个元素都带有一个权值。

单点i j的权值计算方式是(n*n)^(a(i,j))。

玩家从起点1 1开始,只能向右和向下移动。

每次询问会在矩阵中划分一个子矩阵区域,玩家不能走这个区域,询问玩家到达终点可以获得的最大权值。

 

题解:

一个很显然的思路是,建立两个二维DP数组,分别表示每个点距离起点的最大权值和距离终点的最大权值;再开一个二维数组记录每一行的前后缀DP最大值。那么对于每个询问,我们只需要枚举一组前后缀就可以得到答案。

但是如果这道题真的是这个我等凡人都可以想到的思路的话,那也太简单了。Claris大神在出这题的时候加了一个特殊的条件,就是每个点的点权计算公式。这会使得答案非常非常大,而在DP的过程中是不能取模的。这就使得这道题从一道签到题一跃变成了金牌题。

这里题解中给出的思路是,令cnt(i)表示经过一个方案里出现(n*n)^i元素的次数。考虑到指数的性质,我们比较两个方案的大小就可以转化为比较cnt(i)从大到小构成的字符串的字典序的大小!

那么如何比较两个方案的大小呢?Claris大神给出的一种方法是,给线段树增加一个哈希值,这样每个区间都有一个哈希值,对于两个方案递归比较即可。

观察DP过程可以发现,一个新的方案大部分继承上一个方案,cnt(i)单点更新,那么就可以想到用可持久化线段树维护这个过程。同时注意在计算方案的时候不要使用线段树合并,会造成时间复杂度的退化。

这里又有一个细节需要处理,就是一个点由两个方案相加而成,如何避免重复计算这个点的权值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=405;//最大的点数 
const int M=1e7+100;//可持久化线段树的预期点数
const int mod=1e9+7;//模数
int t;//样例数 
int n;//点数 
int q;//询问数
int m;//线段树的预期大小,这里设为n*n
int a[maxn][maxn];//原矩阵
int f[maxn][maxn];//正向DP数组,保存的是每个点相对于起点的最大方案
int g[maxn][maxn];//反向DP数组,保存方案的方式都是线段树的根节点
int w[maxn][maxn];//反向DP数组,但不包含当前节点的权值,这样就避免了重复计算 
int pre[maxn][maxn];//每一行的前缀最大值
int suf[maxn][maxn];//每一行的后缀最大值
//前后缀最大值的保存方式是下标 
int tot;//动态开点
int lson[M];//左儿子 
int rson[M];//右儿子
int c[M];//每种数的出现次数 
ll ans[M];//答案数组
ull Hash[M];//哈希数组
ull P[M];//预处理好的哈希数组
ll weight[M];//预处理好的节点权值数组 
int up (int x,int l,int r,int p,int v) {
    int y=++tot;
    c[y]=c[x]+1;
    ans[y]=(ans[x]+weight[p]*v)%mod;
    Hash[y]=Hash[x]+P[p]*v;
    if (l==r) return y;
    int mid=(l+r)>>1;
    if (p<=mid)
        lson[y]=up(lson[x],l,mid,p,v),rson[y]=rson[x];
    else
        lson[y]=lson[x],rson[y]=up(rson[x],mid+1,r,p,v);
    return y;
} 
int query (int A,int B,int C,int D) {
    //ABCD表示四颗线段树的根节点
    //如果A+B>C+D 返回1
    //这样就省去了合并的时间
    if (Hash[A]+Hash[B]==Hash[C]+Hash[D]) return 0;
    int l=1,r=m;
    while (l<r) {
        int mid=(l+r)>>1;
        if (Hash[rson[A]]+Hash[rson[B]]==Hash[rson[C]]+Hash[rson[D]]) {
            r=mid;
            A=lson[A];
            B=lson[B];
            C=lson[C];
            D=lson[D];
        }
        else {
            l=mid+1;
            A=rson[A];
            B=rson[B];
            C=rson[C];
            D=rson[D];
        }
    }
    return c[A]+c[B]>c[C]+c[D];
}
int main () {
    scanf("%d",&t);
    P[0]=1;
    for (int i=1;i<M;i++) {
        P[i]=P[i-1]*13331;
    }
    while (t--) {
        scanf("%d%d",&n,&q);
        weight[0]=1;
        tot=0;
        m=n*n;
        for (int i=1;i<=m;i++) weight[i]=weight[i-1]*n%mod*n%mod;
        for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&a[i][j]);
        for (int i=0;i<=n+1;i++) for (int j=0;j<=n+1;j++) f[i][j]=g[i][j]=pre[i][j]=suf[i][j]=w[i][j]=0;
        for (int i=1;i<=n;i++) 
            for (int j=1;j<=n;j++) {
                int tt=query(f[i-1][j],0,f[i][j-1],0)?f[i-1][j]:f[i][j-1];
                f[i][j]=up(tt,1,m,a[i][j],1);
            }
        for (int i=n;i;i--) 
            for (int j=n;j;j--)  {
                w[i][j]=query(g[i+1][j],0,g[i][j+1],0)?g[i+1][j]:g[i][j+1];
                g[i][j]=up(w[i][j],1,m,a[i][j],1);
            }
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++) {
                pre[i][j]=query(f[i][pre[i][j-1]],w[i][pre[i][j-1]],f[i][j],w[i][j])==1?pre[i][j-1]:j;
            }
        for (int i=n;i;i--)
            for (int j=n;j;j--) 
                suf[i][j]=query(f[i][suf[i][j+1]],w[i][suf[i][j+1]],f[i][j],w[i][j])==1?suf[i][j+1]:j;
        while (q--) {
            int xl,xr,yl,yr;
            scanf("%d%d%d%d",&xl,&xr,&yl,&yr);
            //比较xl-1行的yr+1后缀和xr+1行的yl-1前缀 
            int tt=query(f[xl-1][suf[xl-1][yr+1]],w[xl-1][suf[xl-1][yr+1]],f[xr+1][pre[xr+1][yl-1]],w[xr+1][pre[xr+1][yl-1]])?1:2;
            if (tt==1) {
                printf("%lld\n",(ans[f[xl-1][suf[xl-1][yr+1]]]+ans[w[xl-1][suf[xl-1][yr+1]]])%mod);
            }
            else {
                printf("%lld\n",(ans[f[xr+1][pre[xr+1][yl-1]]]+ans[w[xr+1][pre[xr+1][yl-1]]])%mod);
            }
        }
        for (int i=0;i<=tot;i++) {
            lson[i]=rson[i]=c[i]=Hash[i]=ans[i]=0;
        }        
    }
}

 

posted @ 2020-09-22 03:29  zlc0405  阅读(264)  评论(0编辑  收藏  举报