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

原文地址:https://www.cnblogs.com/Alan-Luo/p/10175526.html

时间: 2024-11-08 09:47:46

20181225模拟赛 T1 color (转化思想,分拆思想)的相关文章

20180610模拟赛T1——脱离地牢

Description 在一个神秘的国度里,年轻的王子Paris与美丽的公主Helen在一起过着幸福的生活.他们都随身带有一块带磁性的阴阳魔法石,身居地狱的魔王Satan早就想着得到这两块石头了,只要把它们溶化,Satan就能吸收其精华大增自己的魔力.于是有一天他趁二人不留意,把他们带到了自己的地牢,分别困在了不同的地方.然后Satan念起了咒语,准备炼狱,界时二人都将葬身于这地牢里. 危险!Paris与Helen都知道了Satan的意图,他们要怎样才能打败魔王,脱离地牢呢?Paris想起了父王

2017-9-3模拟赛T1 卡片(card)

题目 [题目描述] lrb 喜欢玩卡牌.他手上现在有n张牌,每张牌的颜色为红绿蓝中的一种.现在他有两种操作.一是可以将两张任意位置的不同色的牌换成一张第三种颜色的牌:二是可以将任意位置的两张相同颜色的牌换成一张该颜色的牌.两个操作后都可以将生成的牌放到任意位置.现在他想知道,最后一张牌可能是什么颜色的. [输入描述] 第一入一个n,表示卡牌数量.第二行输入一个由'B','G','R'组成的长度为n的字符串,分别表示卡牌的颜色为蓝色.绿色.红色中的一种.[输出描述]输出'B','G','R'中的若

2017/9/3模拟赛T1

题解:这题是一道判断题,分5种情况讨论,以下为了方便以ABC为例 ①若只有A,答案为A ②若A.B.C都有,答案为ABC ③若只有AB,答案为C ④若AAB式,答案为BC ⑤若只有A.B且AB均大等于2,答案为ABC 代码如下: 1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 int n,cnt[3],cur; 5 char a[205]; 6 bool pd(){ 7 for(int i=0;i&l

2017 11 6模拟赛T1

作为一个毒瘤出题人(wzy:我不是毒瘤出题人,这些题明明很水的),wzy的题干十分复杂,但是把题意简化之后,相当简单粗暴... 求首项为1,等比为m,项数为t的等比数列的和,答案对k取模 不保证m与k互质 如果m与k互质的话,用等比数列的求和公式在求个逆元就能解决了,但是本题显然不能,于是必须考虑不含有除法的算法 于是就有了分治求等比数列和的办法. 设s(x)为等比数列的第n项 由等比数列的性质得到s(y)=s(x)*m^(y-x) (y>x) 将一个长度为2r的等比数列拆分成登场的两部分,对应

[模拟赛] T1 高级打字机

Description 早苗入手了最新的高级打字机.最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧. 请为这种高级打字机设计一个程序,支持如下3种操作: 1.T x:在文章末尾打下一个小写字母x.(type操作) 2.U x:撤销最后的x次修改操作.(Undo操作) (注意Query操作并不算修改操作) 3.Q x:询问当前文章中第x个字母并输出.(Query操作) 文章一开始可以视为空串. Input 第1行:一个整数n,表示操作数量. 以下n行,每行一个命令.保证输入的命令合法

20180527模拟赛T1——新田忌赛马

[问题描述] (注:此题为d2t2-难度) 田忌又在跟大王van赛马的游戏 田忌与大王一共有2n匹马,每个马都有一个能力值x,1<=x<=2n且每匹马的x互不相同.每次田忌与大王放出一匹马,较大的获胜.但是田忌有一个能力,在任何比赛的开始前,他可以把马变成x较小的获胜,并一直持续到比赛结束 田忌可以一直不用这个能力,也可以在第一轮前使用 现在,田忌已经知道了大王的出马顺序,田忌要问聪明的你,他最多能获得几次胜利? [输入格式] 第一行为一个整数:N(1<=N<=50000)接下来

校内模拟赛T1大美江湖

这就是一个模拟题,注意1234分别对应左右上下横坐标和纵坐标的判断就好了 题解: 需要注意的是,向上取整ceil函数是对于一个double值返回一个double值,也就是说在ceil里面的类型一定要是double,否则会炸 代码: #include<cstdio> #include<iostream> #include<cstdlib> #include<cmath> #include<cstring> #include<string>

【2019.8.14 慈溪模拟赛 T1】我不是!我没有!别瞎说啊!(notme)(BFS+DP)

\(IDA^*\) 说实话,这道题我一开始没想出正解,于是写了一个\(IDA^*\)... 但神奇的是,这个\(IDA^*\)居然连字符串长度分别为\(2500,4000\)的数据都跑得飞快,不过数据发下来之后我测了一下只有45分. 就在不断优化\(IDA^*\)的过程中,我突然就想出了正解的做法,看来以后遇事不决先暴力. \(DP\)求解第一个询问 考虑一个\(DP\),我们设\(f_{i,j}\)表示当前在第一个字符串中是第\(i\)位,第二个字符串中是第\(j\)位的最小步数. 若记录\(

【2019.8.15 慈溪模拟赛 T1】插头(plugin)(二分+贪心)

二分 首先,可以发现,最后的答案显然满足可二分性,因此我们可以二分答案. 然后,我们只要贪心,就可以验证了. 贪心 不难发现,肯定会优先选择能提供更多插座的排插,且在确定充电器个数的情况下,肯定选择能经过排插数量最大的那些充电器. 所以,我们只要模拟插排插的过程,记录当前深度\(d\).插座数\(t\)即可. 设选择的能经过排插数量恰好为\(d\)的充电器有\(x\)个,则若\(t<x\),显然不合法. 否则,我们将\(x\)个位置插上充电器,其余位置尽可能地插排插,就可以了. 代码 #incl