常州模拟赛d8t2 绘画

 

 

分析:考虑记录每个坐标上每个颜色出现了几次,并由此算出每个颜色在这个坐标上的贡献。答案肯定是原图的答案扣去矩形的答案,再加上那个矩形同种颜色的贡献,这里的答案指的是Σdis.我们先要记录每个颜色在各个位置出现的次数,因为每一次都是区间操作嘛,所以我们用二维差分可以很好地维护,前缀和求出出现的次数. 然后求出每个位置原本图的和副本的差距,求一下前缀和就得到原本图整体的答案.

最后再用一个前缀和数组求出每个位置覆盖为颜色x后的贡献,就可以了.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

#define s(u,i,j,k,l) (u[k][l] - u[i-1][l] - u[k][j-1] + u[i - 1][j-1])
using namespace std;

const int maxn = 1010,maxm = 300010;
int n, m, kk, rubbish,a[maxn][maxn],sum[30][maxn][maxn],col[maxm];
int x3[maxm], y3[maxm], x4[maxm], y4[maxm];
long long ans[maxn][maxn],b[30][maxn][maxn],ret = 1LL << 60,pi;
char s[maxn];

long long S1(int x, int y, int x2, int y2)
{
    return ans[x2][y2] - ans[x - 1][y2] - ans[x2][y - 1] + ans[x - 1][y - 1];
}

long long S2(int cur, int x, int y, int x2, int y2)
{
    return b[cur][x2][y2] - b[cur][x - 1][y2] - b[cur][x2][y - 1] + b[cur][x - 1][y - 1];
}

int main()
{
    scanf("%d%d%d%d", &n, &m, &kk, &rubbish);
    for (int i = 1; i <= n; i++)
    {
        scanf(" %s", s + 1);
        for (int j = 1; j <= m; j++)
            a[i][j] = s[j] - 'a';
    }
    for (int i = 1; i <= kk; i++)
    {
        scanf("%d%d%d%d %c", &x3[i], &y3[i], &x4[i], &y4[i], &col[i]);
        col[i] -= 'a';
        ++sum[col[i]][x3[i]][y3[i]]; //二维差分修改每种颜色出现的次数
        --sum[col[i]][x3[i]][y4[i] + 1];
        --sum[col[i]][x4[i] + 1][y3[i]];
        ++sum[col[i]][x4[i] + 1][y4[i] + 1];
    }
    for (int k = 0; k < 26; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                sum[k][i][j] += sum[k][i - 1][j] + sum[k][i][j - 1] - sum[k][i - 1][j - 1];//统计每个点每种颜色出现的次数
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            for (int k = 0; k < a[i][j]; k++)
                ans[i][j] += sum[k][i][j] * (a[i][j] - k);//每一位对dis的贡献值
            for (int k = 25; k > a[i][j]; k--)
                ans[i][j] += sum[k][i][j] * (k - a[i][j]);
            ans[i][j] += ans[i - 1][j] + ans[i][j - 1] - ans[i - 1][j - 1];//记录整个图的dis
        }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            int t = 0;
            for (int k = 0; k < 26; k++)
                t += sum[k][i][j];
            sum[a[i][j]][i][j] += kk - t;//之前记录的是副本上出现的次数,现在记录原有的出现次数
        }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            int s1 = 0, s2 = 0;
            for (int k = 0; k < 26; k++)
            {
                s1 += sum[k][i][j] * k;
                s2 += sum[k][i][j];
                b[k][i][j] = s2 * k - s1; //如果我把每个副本(i,j)上的点全部变成k的贡献
            }
            s1 = s2 = 0;
            for (int k = 25; k >= 0; k--)//相当于计算ans,倒着计算一次
            {
                s1 += sum[k][i][j] * k;
                s2 += sum[k][i][j];
                b[k][i][j] += s1 - s2 * k;
            }
            for (int k = 0; k < 26; k++)
                b[k][i][j] += b[k][i - 1][j] + b[k][i][j - 1] - b[k][i - 1][j - 1];
        }
    for (int i = 1; i <= kk; i++)
    {
        long long temp = ans[n][m] - s(ans, x3[i], y3[i], x4[i], y4[i]) + s(b[col[i]], x3[i], y3[i], x4[i], y4[i]);
        if (temp < ret)
        {
            ret = temp;
            pi = i;
        }
    }
    printf("%lld %d\n", ret, pi);return 0;
}

 

 

 

posted @ 2017-10-01 00:34  zbtrs  阅读(288)  评论(0编辑  收藏  举报