题意
给你一个nn的网格。
初始时,每个网格有一个非负权值。有n次操作。
每次操作会将一个网格的权值加1或减1。(权值操作后仍非负)
输出当前每个网格到11的最长路(每步向上或向左)的总和。
n
⩽
2000
n\leqslant 2000
n⩽2000
解法
这是一道好题!
我太菜了,做不出。
我们考虑维护dp值。
令
f
i
,
j
f_{i,j}
fi,j表示到(i,j)的最长路。
再令差分数组
g
i
,
j
=
f
i
−
1
,
j
−
f
i
,
j
−
1
g_{i,j}=f_{i-1,j}-f_{i,j-1}
gi,j=fi−1,j−fi,j−1
考虑一次加1或减1造成的影响。
显然,它会使每一行的一个区间的dp值造成加1或减1的影响。
设这些区间为
{
l
i
,
r
i
}
\{l_i,r_i\}
{li,ri}那么我们有
l
i
⩽
l
i
+
1
,
r
i
⩽
r
i
+
1
l_i\leqslant l_{i+1},r_i\leqslant r_{i+1}
li⩽li+1,ri⩽ri+1
利用差分数组我们可以在
O
(
n
)
O(n)
O(n)的时间内求出所有l和r并且修改对应的差分数组。
完结撒花!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
ll Ans=0;
int n;
#define Maxn 2005
int a[Maxn][Maxn];
int dp[Maxn][Maxn],b[Maxn][Maxn];
int g[Maxn][Maxn];
inline void rd(int &x){
x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
}
inline void Init(){
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j){
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j];
g[i][j]=dp[i-1][j]-dp[i][j-1];
Ans+=dp[i][j];
}
}
inline void Modify(int x,int y,int sgn){
int contain=0;
int l=y,r=y;
for(register int i=y+1;i<=n;++i){
g[x][i]-=sgn;
if(g[x][i]+sgn<0||(g[x][i]+sgn==0&&sgn==1))r=i;
else break;
}
contain=r-l+1;
for(register int i=x+1;i<=n;++i){
while(l<=r){
g[i][l]+=sgn;
if(g[i][l]-sgn<0||(g[i][l]-sgn==0&&sgn==-1))l++;
else break;
}
if(l>r)break;
while(r<n){
g[i][r+1]-=sgn;
if(g[i][r+1]+sgn<0||(g[i][r+1]+sgn==0&&sgn==1))r++;
else break;
}
contain+=r-l+1;
}
Ans+=sgn*contain;
}
char opt[5];
int main(){
rd(n);
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j)rd(a[i][j]);
Init();
printf("%lld\n",Ans);
int x,y;
for(register int i=1;i<=n;++i){
scanf("%s",opt);
rd(x);rd(y);
if(opt[0]=='U')Modify(x,y,1);
else Modify(x,y,-1);
printf("%lld\n",Ans);
}
return 0;
}