传送门
题目大意
给你一个长度为 的 串,你有两种操作:
- 将一个子串反转(reverse),代价为 ;
- 将一个子串翻转(flip),代价为 。
求将这个 串变成全是 的串的最小代价。 和 均为给定整数。
思路
考虑全是 怎么办,显然直接翻转啊!什么意思呢?就是说,连续的一段,你不会去打断它,分成多段去处理,而是直接处理了。
那我们考虑把连续的
和
都缩起来。由于操作是对子串进行的,因此我们忽略两头的
,那么现在这个串长这样:
考虑消去第一个 。可以反转前两个字符:
注意,如果两个相同的字符连了起来,根据前面的理论,需要认为它们缩成了一个字符。
也可以翻转前两个字符:
发现这两种操作是一样的,都可以删去开头的 ,或者说都可以删去一个 。我们的目标是删去所有 ,只留下一个 。发现,不存在别的操作能够删去更多的 (翻转 虽然会消去两个 ,但是又会增加一个 ),所以就这么做就好了。
我们选择代价小的操作一直做就好了。特别地,最后还会剩下一个 ,这个 只能用翻转操作变成 。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
INT_PUT a = 0; bool positive = true;
char ch = getchar();
while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
if (ch == '-') { positive = false; ch = getchar(); }
while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
return positive ? -a : a;
}
void printOut(INT_PUT x)
{
char buffer[20]; int length = 0;
if (x < 0) putchar('-'); else x = -x;
do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
do putchar(buffer[--length]); while (length);
}
const int maxn = int(3e5) + 5;
int n;
int x, y;
char str[maxn];
void run()
{
n = readIn();
x = readIn();
y = readIn();
scanf("%s", str);
n = std::unique(str, str + n) - str;
str[n] = '\0';
int nZero = 0;
for (int i = 0; i < n; i++)
nZero += str[i] == '0';
printOut(((LL)std::min(x, y) * (nZero - 1) + y) * bool(nZero));
}
int main()
{
run();
return 0;
}
总结
别这么心急……仔细推一下性质。这种消 题往往可以从个数的变化入手。
NOI 前 CF 做不来 A 题是一种怎样的体验?