标签:sof 预处理 algorithm scan 复杂 names 消息 mil sign
题目描述
输入
输出
样例输入
8 5 3
1 2 3 4 5 6 7 8
2 5 2 3 4
1 8 3 2 1
5 7 4 5 6
2 5 1 2 3
1 7 3 4 5
样例输出
No
Yes
Yes
Yes
No
题目大意
给出一个长度为n的序列和m个询问,每次询问需要判断给出的长度为k的序列是否在原序列的[l,r]范围内出现过,是则输出"No",否则输出"Yes"。
题解
Hash+STL-map+莫队算法
由于询问序列长度是给定的且不超过20,所以我们可以把原序列中所有长度为k的连续子序列预处理出来,并使用Hash的方法储存起来,放在一起用map离散化。
对于每个询问把它Hash出来,如果它不在原序列中出现过直接答案为0,否则用莫队算法跑一下就好了。
注意每个Hash值对应的离散化的值要先预处理出来,用桶维护出现次数,而不是用 map<unsigned long long , int> cnt 来存储出现次数,否则会TLE。
时间复杂度大概是$O((n+m)(k+\log n)+m\sqrt n)$。
#include <cstdio> #include <cmath> #include <algorithm> #include <map> #define N 100010 using namespace std; typedef unsigned long long ull; const ull base = 1000003; struct data { int l , r , bl , id; ull x; }q[N]; map<ull , int> ref; ull a[N] , val[N]; int pos[N] , cnt[N] , ans[N]; bool cmp(data a , data b) { return a.bl == b.bl ? a.r < b.r : a.bl < b.bl; } int main() { int n , m , k , i , j , si , lp = 1 , rp = 0; ull t; scanf("%d%d%d" , &n , &m , &k) , si = (int)sqrt(n); for(i = 1 ; i <= n ; i ++ ) scanf("%llu" , &a[i]); for(i = 1 ; i <= n - k + 1 ; i ++ ) { for(j = 1 ; j <= k ; j ++ ) val[i] = val[i] * base + a[i + j - 1]; if(ref.find(val[i]) == ref.end()) ref[val[i]] = i; pos[i] = ref[val[i]]; } for(i = 1 ; i <= m ; i ++ ) { scanf("%d%d" , &q[i].l , &q[i].r) , q[i].bl = (q[i].l - 1) / si , q[i].r -= k - 1 , q[i].id = i; for(j = 1 ; j <= k ; j ++ ) scanf("%llu" , &t) , q[i].x = q[i].x * base + t; } sort(q + 1 , q + m + 1 , cmp); for(i = 1 ; i <= m ; i ++ ) { if(ref.find(q[i].x) == ref.end()) continue; while(rp < q[i].r) rp ++ , cnt[pos[rp]] ++ ; while(lp > q[i].l) lp -- , cnt[pos[lp]] ++ ; while(rp > q[i].r) cnt[pos[rp]] -- , rp -- ; while(lp < q[i].l) cnt[pos[lp]] -- , lp ++ ; ans[q[i].id] = cnt[ref[q[i].x]]; } for(i = 1 ; i <= m ; i ++ ) printf("%s\n" , ans[i] ? "No" : "Yes"); return 0; }
【bzoj3207】花神的嘲讽计划Ⅰ Hash+STL-map+莫队算法
标签:sof 预处理 algorithm scan 复杂 names 消息 mil sign
原文地址:http://www.cnblogs.com/GXZlegend/p/7043418.html