Codeforces Round 761F - Dasha and Photos

给出一个 n×m 的只包含小写字母的矩阵 A

k次独立的操作,每次把原矩阵的某个子矩阵用一个字母覆盖得到一个矩阵 Ax (称之为特殊矩阵)。

定义 Ax Ay 的距离 dis(Ax,Ay)=1in,1jm|Axi,jAyi,j|

min1xk{1ym,yxdis(Ax,Ay)}


对于前缀和的应用真是excited

cnt统计的是所有的special矩阵对应位置的字母个数

用原矩阵拿来算贡献放到val里

然后枚举special

对每个special非子矩阵的位置就是所有贡献-子矩阵的贡献(这部分通过预处理出val的前缀和可以O(1)做)

然后长方形的位置的贡献枚举字母通过cnt的前缀和得到字母在子矩阵出现的次数O(26)计算

这样就在 O(26×n×m+26×k) 的时间复杂度内解决了本题


#include<bits/stdc++.h>
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;
}

总结一下

在差分下我们可以更新区间

原数组下可以单点更新 单点询问

在前缀和下可以区间查询

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值