题意
给你一个nn的网格。
初始时,每个网格有一个非负权值。有n次操作。
每次操作会将一个网格的权值加1或减1。(权值操作后仍非负)
输出当前每个网格到11的最长路(每步向上或向左)的总和。
解法
这是一道好题!
我太菜了,做不出。
我们考虑维护dp值。
令
表示到(i,j)的最长路。
再令差分数组
考虑一次加1或减1造成的影响。
显然,它会使每一行的一个区间的dp值造成加1或减1的影响。
设这些区间为
那么我们有
利用差分数组我们可以在
的时间内求出所有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;
}