题意:由01组成的长度为n的子串,AT由0表示,GC由1表示,求一段长度大于等于L且GC率最高的子串的起始终止坐标,若GC率相同,取长度较小,若长度相同,取起始坐标最小。
分析:
1、一个子串(i+1,j)的GC率为(sum[j] - sum[i]) / (j - i),sum[j]为前缀和
上式可以理解为点(i, sum[i])与点(j, sum[j])的斜率表达式。
因此问题可转化为求横坐标之差大于等于L的两点所组成的直线斜率的最大值。
2、https://wenku.baidu.com/view/b97cd22d0066f5335a8121a3.html?from_page=view&from_mod=download
上述论文可知,任何一个点Pt的检查集合中,不可能存在一个对最优结果有贡献的上凸点,因此我们可以删去每一个上凸点,剩下的则是一个下凸折线。
3、利用单调性求过检查点Pt与下凸折线相切的直线,从而得到当前右端点下最优的左端点。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #include<cmath> #include<iostream> #include<sstream> #include<iterator> #include<algorithm> #include<string> #include<vector> #include<set> #include<map> #include<stack> #include<deque> #include<queue> #include<list> #define lowbit(x) (x & (-x)) const double eps = 1e-8; inline int dcmp(double a, double b){ if(fabs(a - b) < eps) return 0; return a > b ? 1 : -1; } typedef long long LL; typedef unsigned long long ULL; const int INT_INF = 0x3f3f3f3f; const int INT_M_INF = 0x7f7f7f7f; const LL LL_INF = 0x3f3f3f3f3f3f3f3f; const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f; const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1}; const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1}; const int MOD = 1e9 + 7; const double pi = acos(-1.0); const int MAXN = 100000 + 10; const int MAXT = 10000 + 10; using namespace std; char s[MAXN]; int sum[MAXN]; deque<int> q; int judge(int a, int b, int c, int d){ return (sum[a] - sum[b]) * (c - d) - (sum[c] - sum[d]) * (a - b); } int main(){ int T; scanf("%d", &T); while(T--){ memset(sum, 0, sizeof sum); q.clear(); int n, L; scanf("%d%d", &n, &L); scanf("%s", s + 1); for(int i = 1; i <= n; ++i){ sum[i] = sum[i - 1] + s[i] - '0'; } int ansl = 0, ansr = L;//最优区间初始化为(1,L) for(int i = L; i <= n; ++i){//枚举右端点 int p = i - L; while(q.size() > 1){//维护下凸折线 int tmpj = q[q.size() - 1]; int tmpk = q[q.size() - 2]; if(judge(tmpj, tmpk, p, tmpj) > 0){//上凸 q.pop_back(); } else{ break; } } q.push_back(p);//在右端加入新的点 while(q.size() > 1){//找与下凸折线的切点 if(judge(i, q[0], i, q[1]) <= 0){ q.pop_front(); } else break; } int tmp = judge(i, q[0], ansr, ansl); if(tmp > 0 || (tmp == 0 && i - q[0] < ansr - ansl)){ ansl = q[0]; ansr = i; } } printf("%d %d ", ansl + 1, ansr); } return 0; }