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 ai≠bi, by performing some of the Q operations.
Constraints
- 1≤N≤200,000
- b consists of 0 and 1.
- 1≤Q≤200,000
- 1≤li≤ri≤N
- If i≠j, either li≠lj or ri≠rj.
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
3 1 0 1 1 1 3
Sample Output 1
1
If you choose to perform the operation, a will become {1,1,1}, for a hamming distance of 1.
思路:比较巧妙的题目,题目求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;
}