题目大意:
给两个 01 串 a 和 b,这两个串定义了一个 n*n 的矩阵,(i,j) 这个位置的值等于 ![]()
Yuri 要从矩阵的 (1,1) 出发,每次只能向下或者向右走一格,并且格子里的数一定是 0 才能走。
出发前可以任意翻转 a 和 b 内的元素,定义 f(x,y) 表示走到 (x,y) 这个格子最少需要翻转的次数,
求
Solution:
只能走 0 的位置,也就是说这个位置对应的 ai 和 bj 一定要相同。
手模一下发现,其实 f(x,y) 就是 a 数组第 1~x 位以及 b 数组第 1~y 位的 0或1 数目的最小值。
记 表示区间 a[1,x] 和 b[1,y] 内 0 的个数,
表示 1 的个数,则所求就是:
这就和下面这题有异曲同工之妙了: 思维/技巧 Gangsta-CSDN博客(但是c0 c1的定义有区别)
同样的技巧,把 Min 拆开:
=
=
对于 拆成两部分处理:
第一部分推出来:
我们将原本位置上的 0 看成 -1 ,原本的 1 不变,记 p1 为转化后 a 数组的前缀和,p2 为 b 的,那么 就等于
=
第二部分 = =
最后两部分相减记得除以二。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200005
int T,n;
int a[N],b[N];
long long ans,p1[N],p2[N],pre1[N],pre2[N];
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d",&n);
ans = 1ll * n * n * (n + 1) / 2ll;
p1[0] = p2[0] = pre1[0] = pre2[0] = 0ll;
for (int i = 1;i <= n;++ i)
{
char s;
scanf(" %c",&s);
a[i] = s - '0';
if(!a[i]) a[i] = -1;
p1[i] = p1[i - 1] + a[i];
}
for (int i = 1;i <= n;++ i)
{
char s;
scanf(" %c",&s);
b[i] = s - '0';
if(!b[i]) b[i] = 1;
else b[i] = -1;
p2[i] = p2[i - 1] + b[i];
}
sort(p1 + 1,p1 + n + 1);
sort(p2 + 1,p2 + n + 1);
for (int i = 1;i <= n;++ i)
pre1[i] = pre1[i - 1] + p1[i],
pre2[i] = pre2[i - 1] + p2[i];
int i = 1;
int j = 0;
long long tmp = 0ll;
while (i <= n && j <= n)
{
while (j < n && p1[i] >= p2[j + 1]) ++ j;
tmp += 1ll * j * p1[i] - pre2[j];
++ i;
}
i = 0,j = 1;
while (i <= n && j <= n)
{
while (i < n && p2[j] > p1[i + 1]) ++ i;
tmp += 1ll * i * p2[j] - pre1[i];
++ j;
}
ans -= tmp / 2ll;
printf("%lld\n",ans);
}
return 0;
}
14万+

被折叠的 条评论
为什么被折叠?



