zoukankan      html  css  js  c++  java
  • CF1479B2 Painting the Array II 题解 [DP+线段树]

    Painting the Array II

    Description:

    ​ Homer likes arrays a lot. Today he is painting an array (a_1, a_2, dots, a_n) with two kinds of colors, white and black. A painting assignment for (a_1, a_2, dots, a_n) is described by an array (b_1, b_2, dots, b_n) that (b_i) indicates the color of (a_i) ((0) for white and (1) for black).

    According to a painting assignment (b_1, b_2, dots, b_n), the array a is split into two new arrays (a^{(0)}) and (a^{(1)}), where (a^{(0)}) is the sub-sequence of all white elements in (a) and (a^{(1)}) is the sub-sequence of all black elements in (a). For example, if (a = [1,2,3,4,5,6]) and (b = [0,1,0,1,0,0]), then (a^{(0)} = [1,3,5,6]) and (a^{(1)} = [2,4]).

    The number of segments in an array (c_1, c_2, dots, c_k), denoted (mathit{seg}(c)), is the number of elements if we merge all adjacent elements with the same value in (c). For example, the number of segments in ([1,1,2,2,3,3,3,2]) is (4), because the array will become ([1,2,3,2]) after merging adjacent elements with the same value. Especially, the number of segments in an empty array is (0).

    Homer wants to find a painting assignment (b), according to which the number of segments in both (a^{(0)}) and (a^{(1)}), i.e. (mathit{seg}(a^{(0)})+mathit{seg}(a^{(1)})), is as small as possible. Find this number.

    中文题面:

    ​ 给你一个长度为 (n) 的数列 (a) ,让你将它划分为两个子序列。分别将两个子序列中的连续的相同元素合并为一个元素,每个子序列的价值为合并后的序列长度。请你最小化两个子序列的价值之和,输出这个最小价值。

    Input:

    ​ The first line contains an integer (n) ((1 leq n leq 10^5)).

    ​ The second line contains (n) integers (a_1, a_2, dots, a_n) ((1 leq a_i leq n)).

    Output:

    ​ Output a single integer, indicating the minimal possible total number of segments.

    Sample Input 1:

    6
    1 2 3 1 2 2
    

    Sample Output 1:

    4
    

    Sample Input 2:

    7
    1 2 1 2 1 2 1
    

    Sample Output 2:

    2
    

    Note:

    ​ In the first example, we can choose (a^{(0)} = [1,1,2,2], a^{(1)} = [2,3]) and (mathit{seg}(a^{(0)}) = mathit{seg}(a^{(1)}) = 2). So the answer is (2+2 = 4).

    ​ In the second example, we can choose (a^{(0)} = [1,1,1,1], a^{(1)} = [2,2,2]) and (mathit{seg}(a^{(0)}) = mathit{seg}(a^{(1)}) = 1). So the answer is (1+1 = 2).

    Hint:

    ​ 对于(100\%)的数据,(1 leq n leq 10^5,1 leq a_i leq n)

    ​ 时间限制: (2s)

    ​ 空间限制: (512M)

    题目分析:

    ​ 看到题目的第一眼想的是贪心,但是搞不出最优策略,于是转而选择DP。

    ​ 当然首先将 (a) 中连续的相同元素合并成一个元素方便考虑。

    ​ 设(f_{i,j})表示当前一个序列以编号 (i) 来结尾,另一个序列以数字 (j) 来结尾(即 (exists k,st.1 leq k < i,a_k=j))

    ​ 那么有以下两种转移方式:

    ​ 1.(f_{i,a_{i-1}}={underset {1 leq j leq n}{operatorname {min} }} {f_{i-1,j} + (a_i eq j)})

    ​ 2.(f_{i,j} = f_{i-1,j} + 1)

    ​ 经观察,我们发现这就是个全局加,单点修改,全局查询,因此可以用线段树维护即可。

    ​ PS:其实可以不用线段树。。。随便搞个堆处理一下就行了。

    ​ 代码如下(马蜂很丑,不喜勿喷)——

    #include<bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    int n,tot,a[maxn],f[maxn<<2],tag[maxn<<2];
    inline void pushdown(int now){
    	tag[now<<1]+=tag[now],tag[now<<1|1]+=tag[now];
    	f[now<<1]+=tag[now],f[now<<1|1]+=tag[now];tag[now]=0;
    }
    inline void pushup(int now){f[now]=min(f[now<<1],f[now<<1|1]);}
    inline void modify(int now,int l,int r,int x,int v){
    	if(l==r){f[now]+=v;return;}pushdown(now);int mid=l+r>>1;
    	if(mid>=x) modify(now<<1,l,mid,x,v);else modify(now<<1|1,mid+1,r,x,v);pushup(now);
    }
    inline void updata(int now,int l,int r,int x,int v){
    	if(l==r){f[now]=v;return;}pushdown(now);int mid=l+r>>1;
    	if(mid>=x) updata(now<<1,l,mid,x,v);else updata(now<<1|1,mid+1,r,x,v);pushup(now);
    }
    inline void build(int now,int l,int r){
    	if(l==r){if(l) f[now]=maxn;return;}int mid=l+r>>1;
    	build(now<<1,l,mid),build(now<<1|1,mid+1,r),pushup(now);
    }
    inline int read(){
    	int ret=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
    	while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();return ret*f;
    }
    int main(){
    	n=read();build(1,0,n);for(register int i=1;i<=n;i++){a[i]=read();if(a[i]!=a[i-1]) a[++tot]=a[i];}for(register int i=1;i<=tot;i++)
    	{modify(1,0,n,a[i],-1);int x=f[1]+1;modify(1,0,n,a[i],1),tag[1]++,f[1]++,updata(1,0,n,a[i-1],x);}cout<<f[1]<<'
    ';
    	return 0;
    }
    
  • 相关阅读:
    准备工作:安装Arduino驱动(Windows)
    关于osEye的URL设计
    Debian 6.0下安装Memcached
    准备工作:购买Arduino
    debian6.0安装后中文字体显示不正常的解决办法
    取消UL和OL符号以及padding和margin后恢复默认值的CSS
    linux中reboot、shutdown、halt等命令详细讲解
    IIS6中应用程序池和Web园,解决Session丢失问题
    [z]C# winForm 程序调用 Java WebService
    [z]HTMLTextBox
  • 原文地址:https://www.cnblogs.com/jiangxuancheng/p/14387864.html
Copyright © 2011-2022 走看看