思维/技巧 Unjust Binary Life

题目链接:Problem - F - Codeforces

题目大意:

给两个 01 串 a 和 b,这两个串定义了一个 n*n 的矩阵,(i,j) 这个位置的值等于  

Yuri 要从矩阵的 (1,1) 出发,每次只能向下或者向右走一格,并且格子里的数一定是 0 才能走

出发前可以任意翻转 a 和 b 内的元素,定义 f(x,y) 表示走到 (x,y) 这个格子最少需要翻转的次数,

求  \sum_{x=1}^{n}\sum_{y=1}^{n} f(x,y)

Solution:

只能走 0 的位置,也就是说这个位置对应的 ai 和 bj 一定要相同。

手模一下发现,其实 f(x,y) 就是 a 数组第 1~x 位以及 b 数组第 1~y 位的 0或1 数目的最小值

记 c_0(x,y) 表示区间 a[1,x] 和 b[1,y] 内 0 的个数,c_1(x,y) 表示 1 的个数,则所求就是:

\sum_{x,y} Min(c_0(x,y),c_1(x,y))

这就和下面这题有异曲同工之妙了: 思维/技巧 Gangsta-CSDN博客(但是c0 c1的定义有区别)

同样的技巧,把 Min 拆开

\sum_{x,y} \frac{c_0(x,y) + c_1(x,y) - |c_0(x,y) - c_1(x,y)|}{2}

\frac{1}{2}\sum_{x,y} x + y - |c_0(x,y) - c_1(x,y)|

对于 \sum_{x,y} x + y - |c_0(x,y) - c_1(x,y)|  拆成两部分处理:

第一部分推出来:\sum_{x,y} x + y = \frac{n^2(n+1)}{2}

我们将原本位置上的 0 看成 -1 ,原本的 1 不变,记 p1 为转化后 a 数组的前缀和,p2 为 b 的,那么 |c_0(x,y) - c_1(x,y)| 就等于 |c_0(x,0) + c_0(0,y) - c_1(x,0) - c_1(0,y)| = |p1_x - p2_y|

第二部分 = \sum_{x = 1}^{n} \sum_{y=1}^{n} |p1_x - p2_{x}| = \sum_{}^{p1_x > p2_y} p1_x - p2_y + \sum_{}^{p2_y < p1_x} p1_x - p2_y

最后两部分相减记得除以二。

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值