题目链接 Alternating Strings II

题意是指给出一个长度为n的01串,和一个整数k,要求将这个01串划分为很多子串(切很多刀),使得每个子串长度不超过k,且每个字串不是01交替出现的串(例如01, 10, 101, 010, 101010这些都是01交替出现的串),求最少需要切多少次

令F[i]代表前i个数所需要切的最少的刀数(从1开始计数),那么有

F[i]  = min{F[j] | |j + 1, i| <= k 且 [j, i]这个子串不是01交替出现的串} + 1

这时O(n^2)的思路,足以解决上一个问题Gym 100712D了(n=1000),但是这个题数据n<=100000,所以首先寻求NlogN的方法。

我们记录以i结尾的且是交替串的最长长度为pre,可以看到在计算f[i]时,有以下几种情况:

1.s[i] == s[i - 1] 这时候没有以i结尾交替出现的串,重置pre=1,f[i] = min{f[i - k], ..., f[i - 1]} + 1  (初始化f[0] = -1,注意f下标是从1开始计数的)

2.s[i] != s[i - 1] 这时候pre++,由于最后一个与前一个形成交替串,所以需要比较pre与k的关系:

1) 若pre >= k 或 pre == i,着说明连续出现的交替串长度>k,或者前i个全是01串,那么此时f[i]只能由f[i - 1]转移(相当于在i-1与i之间切一刀)

2)否则的话pre + 1这个串([i - pre - 1, i])一定不是交替串,那么此时[i - k, i - pre - 1]这个区间的所有点都可以转移到i【1】(最后pre+1个不是交替串,那么最后pre+1+x个一定也不是),所以有f[i] = min{f[i - k], ..., f[i - pre - 1]} + 1,所以可以使用线段树查询区间最小值,复杂度NlogN
【1】可以由i转移到j表示在i的后面切一刀,[i + 1, j]这个区间是合法串(非交替串)

下面是代码:

 //#pragma comment(linker, "/STACK:1677721600")
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf (-((LL)1<<40))
#define lson k<<1, L, (L + R)>>1
#define rson k<<1|1, ((L + R)>>1) + 1, R
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define rep(i, a, b) for(int i = a; i <= b; i ++)
#define dec(i, a, b) for(int i = a; i >= b; i --) template<class T> T MAX(T a, T b) { return a > b ? a : b; }
template<class T> T MIN(T a, T b) { return a < b ? a : b; }
template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b; } //typedef __int64 LL;
typedef long long LL;
const int MAXN = + ;
const int MAXM = ;
const double eps = 1e-;
LL MOD = ;
const double PI = 4.0 * atan(1.0); int t, n, k;
char s[MAXN]; struct SegTree {
int mi[MAXN << ]; void update(int k, int L, int R, int p, int v) {
if(L == R) { mi[k] = v; return ; }
if((L + R) / >= p) update(lson, p, v);
else update(rson, p, v);
mi[k] = min(mi[k << ], mi[k << | ]);
} int query(int k, int L, int R, int l, int r) {
if(l == ) return -;
if(R < l || r < L) return INF;
if(l <= L && R <= r) return mi[k];
return min(query(lson, l, r), query(rson, l, r));
} }st; int main()
{
// FIN;
while(~scanf("%d", &t)) while(t--) {
scanf("%d %d%*c %s", &n, &k, s);
mem0(st.mi);
st.update(, , n, , );
int pre = , val;
rep (i, , n) {
if(s[i - ] == s[i - ]) {
pre = ;
val = st.query(, , n, max(i - k, ), i - );
}
else {
pre ++;
if(pre >= k || pre == i) val = st.query(, , n, i - , i - );
else val = st.query(, , n, max(i - k, ), i - - pre);
}
st.update(, , n, i, val + );
}
cout << st.query(, , n, n, n) << endl;
}
return ;
}

同时,这道题还有O(N)的方法(想了太久才搞出= =!)

在上面的过程中讲到,如果i可以由j转移得到,那么一定可以由j - 1转移得到(前提是|j - 1, i| <= k), 那么如果此时f[j - 1] > f[j],那么在计算f[i]时吗,只要j存在,就不可能由j - 1转移(这时显而易见的),所以我们使用队列维护一个f值不降的序列,并记录下标。

在计算f[i]时,首先将队首与i的区间长度>k的点删掉,然后再根据上面讨论的几种情况判断是根据队首转移还是队尾转移,然后将f[i]入队,再根据f[i]的值维护队列的单调性。

 //#pragma comment(linker, "/STACK:1677721600")
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf (-((LL)1<<40))
#define lson k<<1, L, (L + R)>>1
#define rson k<<1|1, ((L + R)>>1) + 1, R
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define rep(i, a, b) for(int i = a; i <= b; i ++)
#define dec(i, a, b) for(int i = a; i >= b; i --) template<class T> T MAX(T a, T b) { return a > b ? a : b; }
template<class T> T MIN(T a, T b) { return a < b ? a : b; }
template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b; } //typedef __int64 LL;
typedef long long LL;
const int MAXN = + ;
const int MAXM = ;
const double eps = 1e-;
LL MOD = ; const int R = ;
const int S = ;
const int P = ; struct Node {
int id, x;
Node(int _id = , int _x = ) {
id = _id; x = _x;
}
}q[MAXN];
int st = , ed = ;
int T, n, k;
char s[MAXN]; void pop_fr(int i) {
while(st < ed && i - q[st].id > k) st++;
} void pop_ed(int id, int val) {
while(st < ed && q[ed - ].x > val) ed --;
q[ed++] = Node(id, val);
} int main()
{
// FIN;
// FOUT;
int cas = ;
while(~scanf("%d", &T)) while(T--) {
scanf("%d %d%*c %s", &n, &k, s);
int len = strlen(s), pre = ;
int f = ;
st = ed = ;
q[ed ++] = Node(, -);
q[ed ++] = Node(, );
rep (i, , len - ) {
pop_fr(i + );
if(s[i] == s[i - ]) {
f = q[st].x + ;
pre = ;
}
else {
pre ++;
f = (pre >= (i + - q[st].id) || pre == i + ) ? (q[ed - ].x + ) : (q[st].x + );
}
pop_ed(i + , f);
//printf("%d: %d\n", i + 1, f);
}
cout << f << endl;
}
return ;
}

Gym 100712L Alternating Strings II(单调队列)的更多相关文章

  1. Alternating Strings Gym - 100712D 简单dp && Alternating Strings II Gym - 100712L 数据结构优化dp

    比赛链接:https://vjudge.net/contest/405905#problem/D 题意: 给你一个长度为n的由0或1构成的串s,你需要切割这个串,要求切割之后的每一个子串长度要小于等于 ...

  2. Gym - 100712D Alternating Strings

    http://codeforces.com/gym/100712/attachments 题意: 给出一个01串,现在要切割这个01串,使得每个子串长度都不大于k,并且每个子串不能01交替出现,单个字 ...

  3. 单调队列 + 组合数统计 Gym 101102D

    题目链接:http://codeforces.com/gym/101102/problem/D 题目大意:给你一个n*m的矩阵,矩阵里面的数值范围为[1,1e9].这个矩阵有一个值,如果相邻的多个数字 ...

  4. Gym 100801 J. Journey to the “The World’s Start” DP+单调队列优化+二分

    http://codeforces.com/gym/100801 题目大意:有从左到右有n个车站,有n-1种车票,第i种车票一次最多可以坐 i 站(1<=i<=n)   每种票有固定的价钱 ...

  5. Gym - 100570C: Subrect Query (巧妙的单调队列)

    De Prezer loves rectangles.He has a n × m rectangle which there is a number in each of its cells. We ...

  6. Gym 100801J Journey to the "The World's Start"(二分+单调队列)

    题意: 现在有1,2,3...N这N个站, 给定限定时间Limt,  N-1种票的价格, 分别对应一个最远距离,  叫你选择一种票, 满足可以在规定时间到达N站台,而且价格最低 思路: 如果买距离为L ...

  7. Gym 100342E Minima (暴力,单调队列)

    3e7暴力,800ms+过,单调队列维护区间最小值. #include<bits/stdc++.h> using namespace std; typedef long long ll; ...

  8. Gym - 101234J Zero Game (单调队列)

    题意:有一个长度为n的01序列,你可以移动k次,每次将一个数移到任意一个位置,求经过操作后区间连续最大的连续0的个数. “移动”操作看似情况很复杂,不好讨论,但其实无非就两种情况: 一.移动的是1:显 ...

  9. luogu4182 [USACO18JAN] Lifeguards P (单调队列优化dp)

    显然可以先把被覆盖掉的区间去掉,然后排个序,左.右端点就都是单调的 设f[i][j]表示前i个区间中删掉j个,而且钦定i不能删的最大覆盖长度 (如果不钦定,就要有一个删掉的状态,那我无法确定前面的到底 ...

随机推荐

  1. CodeForces - 366C Dima and Salad (01背包)

    题意:n件东西,有属性a和属性b.要选取若干件东西,使得\(\frac{\sum a_j}{\sum b_j} = k\).在这个条件下,问\(\sum a_j\)最大是多少. 分析:可以将其转化为0 ...

  2. hdu 5140 主席树

    这题说的是每个员工有工资 水平 在公司待的年限这几个属性,有大量的查询 查的是在一定的水平和工作年限的工人总工资是多少 这个思路是比较简单的我们按照他们的水平排序,排完后,使用主席树不断地往里面插,然 ...

  3. Flex远程调用机制RemoteObject应用技巧

    转自:http://zerozone.javaeye.com/blog/60846Flex远程调用RemoteObject出现的问题及解答: 本文主要讨论Flex在客户端与J2EE中间层数据交互的过程 ...

  4. log parser分析windows日志

    首先将windows安全日志导出,步骤如下: 运行eventvwr.msc命令,打开windows日志,如下图,将所有事件另存为: 保存完之后是一个.evtx格式的文件,将使用log parser分析 ...

  5. Duilib 创建不规则窗口(转载)

    方法一: 转载:http://blog.csdn.net/chenlycly/article/details/46447297 转载:http://blog.csdn.net/harvic880925 ...

  6. SPOJ BALNUM Balanced Numbers(数位DP+状态压缩)题解

    思路: 把0~9的状态用3进制表示,数据量3^10 代码: #include<cstdio> #include<map> #include<set> #includ ...

  7. JVM的异常处理

    异常处理的两大组成要素:抛出异常和捕获异常.这两大要素共同实现程序控制流的非正常转移. 抛出异常分为:显式和隐式两种. 显式抛异常的主题是应用程序,它指的是在程序中使用 “throw”  关键字.手动 ...

  8. 【Nature 子刊】I型HLA基因中和癌症相关的体细胞突变--转载

    肿瘤的发生与免疫系统的功能密切相关.在免疫系统中,MHC(主要组织相容性复体,majorhistocompatibilitycomplex)是所有生物相容复合体抗原的一种统称.HLA(humanleu ...

  9. 使用javascript模拟常见数据结构(四)

    七.树 树是一种非线性的分层的数据结构,在现实生活中比较常见的例子比如家谱和公司的组织架构图,如下所示: 一个树结构存在着一系列的父子结构,并且有着一个根节点,这种结构本质上表明了一对多的关系. 那, ...

  10. BZOJ 3238 【AHOI2013】 差异

    题目链接:差异 写题时发现这道题当初已经用后缀数组写过了……但是既然学了后缀自动机那就再写一遍吧…… 观察一下题目所给的式子:\[\sum_{1\leqslant i < j \leqslant ...