给出一个 n×m 的只包含小写字母的矩阵 A 。
有 k 次独立的操作,每次把原矩阵的某个子矩阵用一个字母覆盖得到一个矩阵 Ax (称之为特殊矩阵)。
定义 Ax 和 Ay 的距离 dis(Ax,Ay)=∑1≤i≤n,1≤j≤m|Axi,j−Ayi,j|
求 min1≤x≤k{∑1≤y≤m,y≠xdis(Ax,Ay)}
对于前缀和的应用真是excited
cnt统计的是所有的special矩阵对应位置的字母个数
用原矩阵拿来算贡献放到val里
然后枚举special
对每个special非子矩阵的位置就是所有贡献-子矩阵的贡献(这部分通过预处理出val的前缀和可以O(1)做)
然后长方形的位置的贡献枚举字母通过cnt的前缀和得到字母在子矩阵出现的次数O(26)计算
这样就在 O(26×n×m+26×k) 的时间复杂度内解决了本题
#include
using namespace std;
#define LL long long
const LL INF = 0x3f3f3f3f3f3f3f3fll;
const int maxn = 1123;
void integral(LL arr[maxn][maxn],int n,int m){
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= m ; j++){
arr[i][j] += arr[i-1][j];
arr[i][j] += arr[i][j-1];
arr[i][j] -= arr[i-1][j-1];
}
}
}
void derivative(LL arr[maxn][maxn],int n,int m){
for(int i = n ; i >= 1 ; i --){
for(int j = m ; j >= 1 ; j--){
arr[i][j] -= arr[i-1][j];
arr[i][j] -= arr[i][j-1];
arr[i][j] += arr[i-1][j-1];
}
}
}
void add(LL arr[maxn][maxn],int sx,int mx,int sy,int my){
arr[mx+1][my+1]++;
arr[mx+1][sy+1]--;
arr[sx+1][my+1]--;
arr[sx+1][sy+1]++;
}
LL sum(LL arr[maxn][maxn],int sx,int mx,int sy,int my){
return arr[mx][my]
-arr[mx][sy]
-arr[sx][my]
+arr[sx][sy];
}
LL cnt[26][maxn][maxn],times[maxn][maxn];
LL que(int c,int sx,int mx,int sy,int my){
LL ret = 0;
for(int z = 0 ; z < 26 ; z++){
ret += abs(c - z) * sum(cnt[z],sx,mx,sy,my);
}
return ret;
}
LL val[maxn][maxn];
int inp[maxn][maxn];
char input[maxn];
const int maxm = 312345;
int a[maxm],b[maxm],c[maxm],d[maxm],e[maxm];
int main(){
int n,m,k;
scanf("%d %d %d",&n,&m,&k);
for(int i = 1 ; i <= n ; i++){
scanf("%s",input+1);
for(int j = 1 ; j <= m ; j++){
inp[i][j] = input[j] - 'a';
}
}
memset(times,0,sizeof(times));
for(int i = 0 ; i < k ; i++){
scanf("%d %d %d %d %s",&a[i],&b[i],&c[i],&d[i],input);
a[i]--,b[i]--;
e[i] = *input - 'a';
add(times,a[i],c[i],b[i],d[i]);
}
integral(times,n,m);
memset(cnt,0,sizeof(cnt));
for(int i = 1; i <= n ; i++){
for(int j = 1 ; j <= m ; j++){
cnt[inp[i][j]][i][j] += k - times[i][j];
}
}
for(int z = 0 ; z < 26 ; z++)
derivative(cnt[z],n,m);
for(int i = 0 ; i < k ; i++)
add(cnt[e[i]],a[i],c[i],b[i],d[i]);
for(int z = 0 ; z < 26 ; z++)
integral(cnt[z],n,m);
memset(val,0,sizeof(val));
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= m ; j++){
for(int z = 0 ; z < 26 ; z++){
val[i][j] += cnt[z][i][j] * abs(z - inp[i][j]);
}
}
}
integral(val,n,m);
for(int z = 0 ; z < 26 ; z++)
integral(cnt[z],n,m);
LL ans = INF;
for(int i = 0 ; i < k ; i ++){
ans = min(ans,val[n][m]
-sum(val,a[i],c[i],b[i],d[i])
+que(e[i],a[i],c[i],b[i],d[i]));
}
printf("%I64d\n",ans);
return 0;
}
总结一下
在差分下我们可以更新区间
原数组下可以单点更新 单点询问
在前缀和下可以区间查询