常州模拟赛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; }