dp cf 20190614
这个题目一开始看还感觉比较复杂,但是还是可以写,因为这个决策很简单就是对于这个字符串倒置还是不倒置。
然后我不会一维去转移,直接用二维,第二维用01来表示转移和不转移,这样子就很清楚了。
这个题目不太像dp,一开始其实没什么思路,后来问题lj,知道思路,但是不知道怎么处理,最后还是上网查了题解
这个题目网上是用 前缀和+二分查找 来处理的
就是前缀和来记录一段区间1的数量,然后再用二分找到我需要的一段区间往前推最早的1的位置,
然后我们往后推移每一个数字,每一次出现的0都是都会再计算一次前面的那个数字。
其实还是有一点点感觉和自己想的不太一样,这个lower_bound 和 upper_bound 只能算到当前值,不然会出问题,
不过应该只会在k==0这种情况下才会出问题吧。
计数:这个题目计数方法很有意思,就是每次找到一个满足条件的区间,求出这个区间长度,然后这个区间往后推移,如果后面的不破坏这个条件,那么就可以继续++
#include <cstdio> #include <cstdlib> #include <map> #include <cstring> #include <queue> #include <vector> #include <algorithm> #include <string> #include <iostream> #define inf 0x3f3f3f3f3f3f3f3f using namespace std; typedef long long ll; const int maxn = 1e6 + 100; const int mod = 1e9 + 7; ll sum[maxn]; char s[maxn]; int main() { int k; scanf("%d", &k); scanf("%s", s + 1); int len = strlen(s + 1); for(int i=1;i<=len;i++) { sum[i] = sum[i - 1]; if (s[i] == '1') sum[i] += 1; printf("sum[%d]=%d\n", i, sum[i]); } ll ans = 0; for(int i=1;i<=len;i++) { if(sum[i]>=k) { ll st = lower_bound(sum, sum + i, sum[i] - k) - sum; ll ed = upper_bound(sum, sum + i, sum[i] - k) - sum; // printf("ed=%lld st=%lld\n", ed, st); ans += ed - st; // printf("i=%d ans=%lld\n", i, ans); } } printf("%I64d\n", ans); return 0; }
这个题目也是求一个满足条件的区间这样的区间有多少个,和上面那个题目的计数的方法特别像。
一个规律就是每九个必然出现一个满足条件s[n]=s[n+k]=s[n+2*k]
计数:从后面往前面推,如果出现一个满足条件的区间,那么可以更新这个右端点,然后用区间长度减去右端点,求出这个右端点到区间端点的长度。
因为这个计数是这个区间长度是只要包含满足条件的这个区间就可以算进去,所以我们一旦找到了满足条件的区间,那么就可以往后推,即使往后推没有找到满足条件的区间,
也没关系,还是可以加上去。这个不仅避免了重复,而且还节约时间。
#include <cstdio> #include <cstdlib> #include <queue> #include <vector> #include <cstring> #include <algorithm> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn = 3e5 + 10; char s[maxn]; int main() { scanf("%s", s + 1); int len = strlen(s + 1); ll ans = 0; int r = len + 1; for(int i=len;i>=1;i--) { r = min(r, i + 9); for(int j=1;i+2*j<=len;j++) { if(s[i]==s[i+j]&&s[i]==s[i+2*j]) { r = min(r, i + 2 * j); break; } } ans += len - r + 1; } printf("%I64d\n", ans); return 0; }