ARC085:F - NRE(线段树 & dp)^

F - NRE


Time limit : 3sec / Memory limit : 256MB

Score : 1000 points

Problem Statement

You are given a sequence a={a1,…,aN} with all zeros, and a sequence b={b1,…,bN} consisting of 0 and 1. The length of both is N.

You can perform Q kinds of operations. The i-th operation is as follows:

  • Replace each of ali,ali+1,…,ari with 1.

Minimize the hamming distance between a and b, that is, the number of i such that aibi, by performing some of the Q operations.

Constraints

  • 1N200,000
  • b consists of 0 and 1.
  • 1Q200,000
  • 1liriN
  • If ij, either lilj or rirj.

Input

Input is given from Standard Input in the following format:

N
b1 b2  bN
Q
l1 r1
l2 r2
:
lQ rQ

Output

Print the minimum possible hamming distance.


Sample Input 1

Copy
3
1 0 1
1
1 3

Sample Output 1

Copy
1

If you choose to perform the operation, a will become {1,1,1}, for a hamming distance of 1.

题意:一个全为0的数组a,给一个数组b和q个操作,每个操作将数组a指定区间改成1,问合理选择部分操作后使得两个数组的∑ aib最小。

思路:比较巧妙的题目,题目求min{(a0, b1),(a1,b0)},同时计算两数组a的0和b的1,以及a的1,b的0比较麻烦,考虑化简一下,即求min{(a0,b1), (b0)-(a0,b0)},b0(b数组0的数量)是常数,那么求min{(a0,b1)-(a0,b0)}即可。这时候可以忽略a数组为1的情况了,因此可以dp去做。dp[i]表示前i个数的(a0,b1)-(a0,b0)最小值,为了方便快速地进行状态转移,用线段树进行加速,具体的思路做法参考代码。

# include <iostream>
# include <cstdio>
# include <cstring>
# include <vector>
# define lson l, m, id<<1
# define rson m+1, r, id<<1|1
using namespace std;
const int maxn = 2e5+30;
const int oo = 0x3f3f3f3f;
int imin[maxn<<2], cost[maxn], dp[maxn];
void build(int l, int r, int id)
{
    if(l == r)
    {
        imin[id] = oo;
        return;
    }
    int m = l+r>>1;
    build(lson);
    build(rson);
    imin[id] = min(imin[id<<1], imin[id<<1|1]);
}

int query(int L, int R, int l, int r, int id)//线段树区间维护最小值。
{
    if(L<=l && r<=R) return imin[id];
    int m = l+r>>1, res = oo;
    if(L <= m) res = min(res, query(L, R, lson));
    if(R > m) res = min(res, query(L, R, rson));
    return res;
}

void update(int pos, int val, int l, int r, int id)
{
    if(l == r)
    {
        imin[id] = min(imin[id], val);
        return;
    }
    int m = l+r>>1;
    if(pos <= m) update(pos, val, lson);
    else update(pos, val, rson);
    imin[id] = min(imin[id<<1], imin[id<<1|1]);
}
vector<int>v[maxn];
int main()
{
    int n, q, cnt=0;
    scanf("%d",&n);
    build(1, n, 1);
    for(int i=1, j; i<=n; ++i)
    {
        scanf("%d",&j);
        cnt += !j;
        cost[i] = j?1:-1;
    }
    scanf("%d",&q);
    for(int i=0, j, k; i<q; ++i)
    {
        scanf("%d%d",&j,&k);
        v[j].push_back(k);//此处需要枚举左端点。
    }
    memset(dp, oo, sizeof(dp));
    dp[0] = 0;
    for(int i=1; i<=n; ++i)
    {
        for(auto j : v[i])
        {
            int mi = dp[i-1];
            mi = min(mi, query(max(i-1,1), j, 1, n, 1));
            if(mi < dp[j])//选择该区间的情况,★为什么可以直接取mi值呢?因为mi值所在点前面枚举时必然覆盖了i点!可以作为答案更新给dp[j]。
            {
                dp[j] = mi;
                update(j, mi, 1, n, 1);
            }
        }
        dp[i] = min(dp[i], dp[i-1]+cost[i]); //不选择该区间的情况。
    }
    printf("%d\n",dp[n]+cnt);
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值