1 条题解
-
0
前置知识:
SA 数组。
题意:
-
给出 个在 内的数 。
-
次询问,每次给出 ,问是否 使 ,。
-
。
为叙述方便,设以 的第 个元素为首个元素的后缀为 。
分析:
- Subtask 0:。
暴力枚举每一个 进行判断。
- Subtask 1:。
是 的 KMP 写法,并不是正解。
对于每一个询问,设 (),那么如果存在 满足 ,就满足条件,否则不满足。
原问题转换成了能否在 中找到一个子串和 相同,可以用 KMP 解决。这个过程是 的, 次询问就是 。
这个 Subtask 虽然和正解使用的算法不同,但对思路有一定帮助。
- Subtask 2: 相同。
若 相同,就说明 相同。所以每次得出的 是另一个串 () 的子串。问题转换成求 的一个子串是否在 中出现过。
对 和 一起求 SA,假设询问的 的子串是 ,那么只需要判断 与 的所有后缀的 lcp 的最大值是否不小于 即可。
可以二分求以 开始的后缀在 rank 中的前驱和后继,复杂度 ,已经能过了。
- Subtask 3:无限制。
注意到 , 可以推出 ,即差分数组是相反数。
求出差分数组 以及差分的相反数数组 。问题等价于求所有满足 的 和 的最长公共前缀的最大值是否不小于 。
以样例为例,各数组如下表:
1 2 3 4 5 6 1 3 4 2 1 0 2 1 -2 -1 / -2 -1 2 1 对于询问三,。
满足 的 有 ,,。所有 为:,,。
其中 与 的公共前缀长度为 。这意味着 ,,并且由于因为已经保证了 ,所以:
,,满足条件。
注意到所有 的 是相同的
废话,所以可以对于每一个 ,将所有 的 放在一个数组里,按 rank 排序,询问时二分 的 rank 在哪个位置即可。
思路:
-
求出差分数组与差分的相反数数组。
-
对于每一个询问,求出 $\max\limits_{a_b+a_l=s}\{\operatorname{lcp}(\operatorname{suff}(a',l),\operatorname{suff}(d',b))\}$ 是否不小于 。
#include <bits/stdc++.h> #define rep(i, l, r) for(int i=l; i<=r; ++i) #define rrep(i, r, l) for(int i=r; i>=l; --i) #define ll long long #define il inline #pragma GCC diagnostic ignored "-Wparentheses" using namespace std; const int mN=4e5+100, mM=2*mN, mD=21; il int read() { int res=0; char c=getchar(); while(c<'0' || c>'9') c=getchar(); while(c>='0' && c<='9') res=(res<<1)+(res<<3)+(c^48), c=getchar(); return res; } il int max_(int a, int b) {return a>b? a: b;} il int min_(int a, int b) {return a<b? a: b;} int n, q, m, a[mN], lg[mM], mn[mD][mM], f[mN], fl[mN], fr[mN]; int c[mM]; il int cal_min(int l, int r) {return min(mn[lg[r-l+1]][l], mn[lg[r-l+1]][r-(1<<lg[r-l+1])+1]);} //begin 板子 int ork, rk[2][mM*2], rak[mM], sa[mM], lcp[mM], buc[mM], tmp1[mM], tmp2[mM]; il void get_buc(int *a) { rep(i, 0, ork) buc[i]=0; rep(i, 1, m) ++buc[a[i]]; rep(i, 1, ork) buc[i]+=buc[i-1]; } void get_sa() { rep(i, 1, m) rk[0][i]=c[i]; int t=0; ork=m; for(; t<=lg[m]; ++t) { get_buc(rk[t&1]+(1<<t)); rep(i, 1, m) tmp1[buc[rk[t&1][i+(1<<t)]]--]=i; get_buc(rk[t&1]); rrep(i, m, 1) tmp2[buc[rk[t&1][tmp1[i]]]--]=tmp1[i]; ork=0; rep(i, 1, m) { if(rk[t&1][tmp2[i]]!=rk[t&1][tmp2[i-1]] || rk[t&1][tmp2[i]+(1<<t)]!=rk[t&1][tmp2[i-1]+(1<<t)]) ++ork; rk[t&1^1][tmp2[i]]=ork; } if(ork==m) {++t; break;} } rep(i, 1, m) sa[rak[i]=rk[t&1][i]]=i; } #define j lcp[rak[i]] il void get_lcp() {rep(i, 1, m) for(j=max_(lcp[rak[i-1]]-1, 0); c[i+j]==c[sa[rak[i]+1]+j]; ++j);} #undef j //end 板子 il bool sol(int s, int l, int r) { //处理询问 (s,l,r) int v=s-a[l], L=fl[v], R=fr[v]; if(v<=0 || v>n || fl[v]>fr[v]) return 0; //没有 a[i]=v, 直接返回 while(L<=R) if(f[L+R>>1]<rak[l]) L=(L+R>>1)+1; else R=(L+R>>1)-1; //二分 rak[l] 的位置 //二分的结果满足 f[R]<rak[l]<f[L] return max_(fl[v]<=R? cal_min(f[R], rak[l]-1): 0, L<=fr[v]? cal_min(rak[l], f[L]-1): 0)>=r-l; //如果在范围内就求 lcp,否则为 0 } int main() { n=read(), q=read(), m=2*n; rep(i, 2, m) lg[i]=lg[i>>1]+1; rep(i, 1, n) a[i]=read(); rep(i, 1, n-1) c[i]=a[i+1]-a[i]+n+1, c[i+n]=a[i]-a[i+1]+n+1; //c[1]~c[n] 为 a', c[n+1]~c[2n] 为 d' c[n]=c[m]=1; get_sa(), get_lcp(); //SA rep(i, 1, m) mn[0][i]=lcp[i]; rep(t, 1, lg[m]) rep(i, 1, m-(1<<t)+1) mn[t][i]=min_(mn[t-1][i], mn[t-1][i+(1<<t-1)]); //ST rep(i, 0, n) buc[i]=0; rep(i, 1, n) ++buc[a[i]]; rep(i, 1, n) buc[i]+=buc[i-1]; rep(i, 1, n) fl[i]=buc[i-1]+1, fr[i]=buc[i]; //值为 v 的 a[i] 在 fl[v] 到 fr[v] 的范围内 rep(i, 1, n) f[buc[a[i]]--]=rak[i+n]; //把 a[i]=v 的 rank[d'[i]] 放进 f[fl[v]~fr[v]] rep(i, 1, n) if(fl[i]<=fr[i]) sort(f+fl[i], f+fr[i]+1); int s, l, r; do s=read(), l=read(), r=read(), puts(sol(s, l, r)? "Yes": "No"); while(--q); return 0; }
特别申明:本题解为团队成员 ATue 所写。
-
信息
- ID
- 184
- 时间
- 800ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 24
- 已通过
- 10
- 上传者