20181225模拟赛 T1 color (转化思想,分拆思想)

Posted alan-luo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20181225模拟赛 T1 color (转化思想,分拆思想)相关的知识,希望对你有一定的参考价值。

题目:

  有?块有 n 段的栅栏,要求第 i 段栅栏最终被刷成颜 ci 。每?次可以选择 l, r 把第l . . . r 都刷成某种颜,后刷的颜?会覆盖之前的。?共有 m 种颜色,雇主知道只需要用m 次就能达成目标,因此你只能刷 m 次。但是你还是可以想办法磨洋工,你希望最?化 m 次刷漆选择的区间长度(r ? l + 1)总和。

 

分析:

  这个题目有一种似曾相识的感觉……

  罢了……人生三大错觉——手机振动、有人敲门、这题我会……

  这个题呢,题目里有提到,只需要m次即可完成涂色,这说明什么?

  每种颜色最多只需涂色一次,这个条件可以转化为,当序列中出现两个颜色相同时,绝对不存在一对相同的颜色与这一对交叉。

  可以举个栗子,有可能出现“1 2 3 1”这样的颜色序列,但绝不可能出现“1 2 1 2”这样的子序列(如果出现了,m对无法将全部颜色染齐),所以如果序列中有颜色相同,那么相同的颜色绝对是一层一层的(感性理解),像这样“1 2 3 3 2 4 2 1”。

  我们不能无脑乱涂,如果层数多了,先涂哪种颜色对我们最终的答案都是有影响的,我们的程序根本处理不了这样的智能问题。

  那么一个思路就出来了。我们是否可以把每种颜色(一定有一个出现的最左端点和一个最右端点)和它横跨的长度抠出来,一部分一部分的计算?

  比如这样一个序列(稍微简单一些):1 2 4 3 2 6 5 2 7 8 9

  可以看到,重复出现的颜色只有2,我们就用这个例子理解一下这种思想

  假如一个序列中颜色尽不相同,像“1 2 3 4……”这种,是不是很好解决,直接从一段开始,每次将一个颜色一直染到另一端。

  我们将上面那个序列拆分后也成为这种感觉,只不过在“1 2 3 4……”这个序列,每一个颜色的长度都是1,对答案的贡献都是1,我们将上面那个序列拆分后,拆成这样:

  ① 1 (2 * * * * * *) 7 8 9

  ② 4 3

  ③ 2 6

   “*”号代表我们拆出来的序列①中,2这个颜色占有的长度范围,这样,我们就简化了原来的序列,但是,序列①和序列②③不同的地方又在于,我们不能无脑地从一段开始“扫射”,因为对于不同的涂法,最后“磨洋工”的时间也是不同的,但我们发现,这种元素相互之间贡献简单的序列,可以被我们拿来做区间dp,所以又套了一个区间dp的思想,也成功神化了这道题。

  这样呢,把我们拆出来的每一个序列单独跑区间dp,每次贡献答案,即是结果了。

  实现难度主要在维护一个p数组,其他的没什么难度,我会在代码里给注释的。

  (这份代码是我抄的std,感谢此题的代码贡献者,写出了能让我读懂的代码)

代码:

 

技术分享图片
 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int N=100005,M=5005;
 5 int a[N],n,m,cnt,l[M],r[M],p[N];
 6 ll s[M],f[M][M];
 7 ll calc(){
 8     for(int i=1;i<=cnt;i++) s[i]+=s[i-1];
 9     for(int i=cnt;i;i--)
10     for(int j=i;j<=cnt;j++)
11     f[i][j]=max(f[i][j-1],f[i+1][j])+s[j]-s[i-1];
12     return f[1][cnt];
13 } int main(){
14 //    freopen("color.in","r",stdin);
15 //    freopen("color.out","w",stdout);
16     scanf("%d%d",&n,&m);ll ans=0;
17     for(int i=1;i<=n;i++) 
18     scanf("%d",&a[i]);
19     for(int i=1;i<=n;i++) r[a[i]]=i;
20     for(int i=n;i;i--) l[a[i]]=i;
21     for(int i=1;i<=m;i++) p[l[i]]=i;
22     for(int i=1;i<=n;i++)
23     if(p[i]){//p数组存的是这个位置是
24         cnt=0;int x=i;//哪个字母出现的最左端
25         while(p[x]){
26             int y=p[x];
27             p[x]=0;s[++cnt]=r[y]-l[y]+1;
28             x=r[y]+1;
29         } ans+=calc();
30     } cout<<ans<<endl;
31     return 0;
32 }
color

 







以上是关于20181225模拟赛 T1 color (转化思想,分拆思想)的主要内容,如果未能解决你的问题,请参考以下文章

python20181225——面试题

R语言使用colorblinr包模拟色盲视觉将已有的ggplot2可视化结果图像使用edit_colors函数编辑转化为色盲视觉友好的可视化结果并自定设置色盲形式色盲严重级别

校内模拟测试010T1 删点游戏dt

排序计数类题目小结

CSPS模拟 59

NOIP模拟77