给一个只有1和2的序列,每次询问有没有一个子串的和为x
SPJ对于格式的要求较为严格。对于每个询问后,应紧跟一个换行符。在最后一次输出你的答案以及一个换行符后不应有任何输出。
由zhouyonglong提供SPJ
题解
首先因为元素只能为1/2,所以我们可以思考如何利用上这个性质,可以发现如下重要性质
若存在一个和为\(k\)的区间,则一定有一个和为\(k-2\)的区间
证明:设这个区间为\([l,r]\),则对\(l,r\)两个位置上的权值分类讨论,若二者其中至少有一个权值为2,那么就把那个端点删去就得到了一个和为\(k-2\)的区间,而若二者的权值都为1,从两端删去即可
那么我们就可以考虑将一个足够大的区间往回缩,不断递归,可以得到与此区间的和的奇偶性相同且小于的所有值所对区间
到了这里,做法就很显然了,先找到最大奇数段和最大偶数段,然后递归预处理每个值的答案区间即可,复杂度只有\(O(N)\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 2000050
#define scanf scanf_s
struct node {
int l, r;
}ans[N];
int val[N], n, m, q, s1[N], s2[N], pos1, pos2;
void dfs(int l, int r, int k) {
if (ans[k].l)return;
if (l > r)return;
ans[k] = { l,r };
if (val[l] == 2) {
dfs(l + 1, r, k - 2);
}
else if (val[r] == 2) {
dfs(l, r - 1, k - 2);
}
else dfs(l + 1, r - 1, k - 2);
}
void init() {
memset(ans, 0, sizeof ans);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
char x;
cin >> x;
if (x == 'T')val[i] = 2;
else val[i] = 1;
}
for (int i = 1; i <= n; i++)s1[i] = s1[i - 1] + val[i];
for (int i = n; i >= 1; --i)s2[i] = s2[i + 1] + val[i];
for (int i = 1; i <= n; i++)if (val[i] == 1) { pos1 = i + 1; break; }
for (int i = n; i > 0; --i)if (val[i] == 1) { pos2 = i - 1; break; }
if (s2[pos1] > s1[pos2]) { dfs(pos1, n, s2[pos1]); }
else { dfs(1, pos2, s1[pos2]); }
dfs(1, n, s1[n]);
}
int main() {
init();
while (m--) {
int k;
scanf("%d", &k);
if (ans[k].l == 0)puts("NIE");
else printf("%d %d\n", ans[k].l, ans[k].r);
}
return 0;
}
标签:val,int,题解,dfs,else,ans,pos2,P3514
From: https://www.cnblogs.com/oierpyt/p/16940106.html