BZOJ 2754 SCOI 2012 喵星球上的点名 后缀数组 树状数组
2754: [SCOI2012]喵星球上的点名
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 2068 Solved: 907
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
6 8 25 0 24 14 8 6 18 0 10 20 24 0
7 14 17 8 7 0 17 0 5 8 25 0 24 0
4 8 25 0 24
4 7 0 17 0
4 17 0 8 25
Sample Output
1
0
1 2
【提示】
事实上样例给出的数据如果翻译成地球上的语言可以这样来看
2 3
izayoi sakuya
orihara izaya
izay
hara
raiz
HINT
【数据范围】
对于30%的数据,保证:
1<=N,M<=1000,喵星人的名字总长不超过4000,点名串的总长不超过2000。
对于100%的数据,保证:
1<=N<=20000,1<=M<=50000,喵星人的名字总长和点名串的总长分别不超过100000,保证喵星人的字符串中作为字符存在的数不超过10000。
Solution1
把名字和点名串用不同的字符连在一起,给对应名字的后缀打标记,做出后缀数组后,对于每个询问,找出其rank值,在sa中左右两边找,用一个数组统计答案即可。
这个东西是很暴力的,但是,数据很水,就可以水过了。
打的时候,把x设了值,又把x、y换了,调了很久。
Solution2
上面的做法是比较不稳定的,看数据,下面的做法则是稳定的O(nlogn)-----(十分感谢Semiwaker大佬的指导)。
我们可以发现,对于一个询问,那么它在sa数组中对应了一段可行的区间,需要统计这个区间内有多少个属于不同名字的后缀。
另外,我们也需要知道,属于同一个名字的后缀,被多少个区间所覆盖。
对于第一个问题,我们可以利用扫描线的方法来解决。对于每个名字所对应的后缀,我们只去计算它最后的那一个。但这个要怎么搞呢?
我们可以把所有的区间按右端点为第一关键字,左端点为第二关键字排序。假设当前遇到了一个后缀,我们就看同名字的后缀前面有没有出现过,如果没有,就直接在该位置+1;否则就把前面出现过的最后的那个后缀的位置-1,再在当前位置上+1。这个可以用一个树状数组来维护,遇到询问区间,只需要区间求和就好。
对于第二个问题,我们依然可以利用扫描线的方法来解决。对于每个名字所对应的的后缀,我们只计算它在该询问区间中,出现的最左的那一个就好。
我们可以把所有区间化为左端点+1、右端点-1。那么要如何统计呢?我们记录每一个名字当前出现的最迟的后缀的编号,再做一个区间求和就可以了,这个可以用树状数组来完成。
总的时间复杂度为O(nlogn),但似乎跑得比暴力还要慢,是我写得不好吗?
Code1
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <set> using namespace std; #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define DWN(i, a, b) for (int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define mset(a, b) memset(a, b, sizeof(a))
const int maxn = ;
int sa[maxn], h[maxn], rk[maxn], num[maxn];
int w_a[maxn], w_b[maxn], w_v[maxn], w_s[maxn];
int bel[maxn];
struct Node
{
int len, start;
}d[maxn];
int vis[maxn], ans[maxn];
int temp[maxn], t_cnt; bool cmp(int *x, int a, int b, int l)
{
return x[a+l] == x[b+l] && x[a] == x[b];
} void da(int n, int m)
{
int *x = w_a, *y = w_b, *t; t_cnt = ;
REP(i, , n) x[i] = num[i];
REP(i, , m) w_s[i] = ;
REP(i, , n) w_s[x[i]] ++;
REP(i, , m) w_s[i] += w_s[i-];
DWN(i, n, ) sa[w_s[x[i]] --] = i;
for (int j = , p = ; p != n; m = p, j *= )
{
p = ;
REP(i, n-j+, n)
y[++p] = i;
REP(i, , n)
if (sa[i]-j > )
y[++p] = sa[i]-j;
REP(i, , n) w_v[i] = x[y[i]];
REP(i, , m) w_s[i] = ;
REP(i, , n) w_s[w_v[i]] ++;
REP(i, , m) w_s[i] += w_s[i-];
DWN(i, n, ) sa[w_s[w_v[i]] --] = y[i];
p = , t = x, x = y, y = t, x[sa[]] = ; //千万注意啊,不能把交换往后挪啊
REP(i, , n) x[sa[i]] = cmp(y, sa[i], sa[i-], j) ? p : ++p;
}
} void calc_height(int n)
{
REP(i, , n) rk[sa[i]] = i;
int k = ;
REP(i, , n)
{
if (k) k --;
int j = sa[rk[i]-];
while (num[i+k] == num[j+k] && i+k <= n && j+k <= n) k ++;
h[rk[i]] = k;
}
} int main()
{
int len = , n, m, breaker = ;
scanf("%d %d", &n, &m);
REP(i, , n)
{
int l;
scanf("%d", &l);
REP(j, , l)
scanf("%d", &num[++len]), bel[len] = i, num[len] ++;
num[++len] = ++breaker;
scanf("%d", &l);
REP(j, , l)
scanf("%d", &num[++len]), bel[len] = i, num[len] ++;
num[++len] = ++breaker;
}
REP(i, , m)
{
scanf("%d", &d[i].len);
d[i].start = len+;
REP(j, , d[i].len)
scanf("%d", &num[++len]), num[len] ++;
num[++len] = ++breaker;
}
da(len, breaker);
calc_height(len);
REP(i, , m)
{
int l = rk[d[i].start], r = rk[d[i].start], now_l = d[i].len;
while (l >= && min(h[l], now_l) >= d[i].len)
now_l = min(now_l, h[l]), l --;
now_l = len;
while (r+ <= n && min(now_l, h[r+]) >= d[i].len)
r ++, now_l = min(now_l, h[r]);
int cnt = ;
REP(j, l, r)
if (vis[bel[sa[j]]] != i && bel[sa[j]] != )
{
vis[bel[sa[j]]] = i;
cnt ++;
ans[bel[sa[j]]] ++;
}
printf("%d\n", cnt);
}
REP(i, , n)
printf("%d%c", ans[i], i == n ? '\n' : ' ');
return ;
}
Code2
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector> using namespace std; #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define DWN(i, a, b) for (int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define mset(a, b) memset(a, b, sizeof(a))
const int maxn = ;
int n, m, bel[maxn], num[maxn];
int w_a[maxn], w_b[maxn], w_v[maxn], w_s[maxn], sa[maxn], rk[maxn], h[maxn], st[maxn][];
struct Node
{
int len, start, l, r, id;
bool operator < (const Node &AI) const { return r == AI.r ? l < AI.l : r < AI.r; }
}query[maxn];
int c[maxn], las[maxn], ans1[maxn], ans2[maxn], add[maxn];
vector <int> del[maxn]; bool cmp(int *x, int a, int b, int l) { return x[a] == x[b] && x[a+l] == x[b+l]; } void get_sa(int n, int m)
{
int *x = w_a, *y = w_b, *t;
REP(i, , m) w_s[i] = ;
REP(i, , n) w_s[x[i]=num[i]] ++;
REP(i, , m) w_s[i] += w_s[i-];
DWN(i, n, ) sa[w_s[x[i]]--] = i;
for (int j = , p = ; p != n; j *= , m = p)
{
p = ;
REP(i, n-j+, n) y[++p] = i;
REP(i, , n) if (sa[i]-j > ) y[++p] = sa[i]-j;
REP(i, , n) w_v[i] = x[y[i]];
REP(i, , m) w_s[i] = ;
REP(i, , n) w_s[w_v[i]] ++;
REP(i, , m) w_s[i] += w_s[i-];
DWN(i, n, ) sa[w_s[w_v[i]]--] = y[i];
t = x, x = y, y = t, p = , x[sa[]] = ;
REP(i, , n) x[sa[i]] = cmp(y, sa[i], sa[i-], j) ? p : ++p;
}
} void get_h(int n)
{
REP(i, , n) rk[sa[i]] = i;
int k = ;
REP(i, , n)
{
if (k) k --;
int j = sa[rk[i]-];
while (num[i+k] == num[j+k] && i+k <= n && j+k <= n) k ++;
h[rk[i]] = k;
}
REP(i, , n) st[i][] = h[i];
REP(j, , )
REP(i, , n)
if (i+(<<(j-)) <= n)//必须判断,不然会RE
st[i][j] = min(st[i][j-], st[i+(<<(j-))][j-]);
} int calc(int l, int r)
{
l ++;
int k = int(log2(r-l+));
return min(st[l][k], st[r-(<<k)+][k]);
} int lowbit(int x) { return x & -x; } void c_modify(const int &n, int x, int d) { while (x <= n) c[x] += d, x += lowbit(x); } int c_query(const int &n, int x)
{
int ret = ;
while (x > ) ret += c[x], x -= lowbit(x);
return ret;
} int main()
{
scanf("%d %d", &n, &m);
int breaker = , len = ;
REP(i, , n)
{
int l;
scanf("%d", &l);
REP(j, , l) scanf("%d", &num[++len]), bel[len] = i, num[len] ++;
num[++len] = ++breaker;
scanf("%d", &l);
REP(j, , l) scanf("%d", &num[++len]), bel[len] = i, num[len] ++;
num[++len] = ++breaker;
}
REP(i, , m)
{
scanf("%d", &query[i].len), query[i].id = i;
query[i].start = len+;
REP(j, , query[i].len) scanf("%d", &num[++len]), num[len] ++;
num[++len] = ++breaker;
}
get_sa(len, breaker), get_h(len);
REP(i, , m)
{
int k = rk[query[i].start], l = , r = k;
query[i].l = query[i].r = k;
while (l <= r)
{
int mid = (l+r)>>;
if (calc(mid, k) >= query[i].len) query[i].l = mid, r = mid-;
else l = mid+;
}
l = k+, r = len;
while (l <= r)
{
int mid = (l+r)>>;
if (calc(k, mid) >= query[i].len) query[i].r = mid, l = mid+;
else r = mid-;
}
}
sort(query+, query+m+);
int now = ;
REP(i, , len)
{
int k = sa[i];
if (bel[k] != )
{
if (las[bel[k]] != ) c_modify(len, las[bel[k]], -);
las[bel[k]] = i, c_modify(len, i, );
}
while (query[now].r == i && now <= m)
ans1[query[now].id] = c_query(len, i)-c_query(len, query[now].l-), now ++;
}
mset(c, ), mset(las, );
REP(i, , m) add[query[i].l] ++, del[query[i].r+].push_back(query[i].l);
REP(i, , len)
{
int k = sa[i];
c_modify(len, i, add[i]);
REP(j, , del[i].size()-) c_modify(len, del[i][j], -);//这段尤为注意,必须一边做一边删
//否则,就会出现减出负数的情况
if (bel[k] != )
{
if (las[bel[k]] != ) ans2[bel[k]] -= c_query(len, las[bel[k]]);
ans2[bel[k]] += c_query(len, i), las[bel[k]] = i;
}
}
REP(i, , m) printf("%d\n", ans1[i]);
REP(i, , n) printf("%d%c", ans2[i], i == n ? '\n' : ' ');
return ;
}
BZOJ 2754 SCOI 2012 喵星球上的点名 后缀数组 树状数组的更多相关文章
- 【BZOJ 2754】[SCOI2012]喵星球上的点名
[链接]h在这里写链接 [题意] n个人; 由姓和名组成.s1[i]和s2[i]; 有m个询问串. 问你第j个询问串,是否为某个人的姓或者名的子串. 如果是的话 ...
- 解题:SCOI 2012 喵星球上的点名
题面 初见广义SAM 建立广义SAM,每次把询问走一遍,最终走到节点的子树里的猫老师都被这次点名点到 这样DFS parent树打时间戳记录入栈出栈时间,把问题转化成一个序列问题:给一个若干种颜色构成 ...
- BZOJ 2754: [SCOI2012]喵星球上的点名 [后缀数组+暴力]
2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1906 Solved: 839[Submit][St ...
- BZOJ 2754: [SCOI2012]喵星球上的点名
2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 649 Solved: 305[Submit][Sta ...
- BZOJ 2754: [SCOI2012]喵星球上的点名 [AC自动机+map+暴力]
2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1902 Solved: 837[Submit][St ...
- BZOJ 2754 【SCOI2012】 喵星球上的点名
题目链接:喵星球上的点名 首先可以发现姓和名两个串就是逗你玩的.在两个串中间插入一个\(10001\),当成一个串做就可以了. 于是我们的问题转化为了: 有\(n\)个串\(A_1,A_2,\dots ...
- BZOJ_2754__[SCOI2012]_喵星球上的点名_(暴力+后缀数组)
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2754 给出n个姓名串和m个点名串.求每个点名串在多少人的姓名中出现过(在名中出现或在姓中出现, ...
- BZOJ2754: [SCOI2012]喵星球上的点名
2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 680 Solved: 314[Submit][Sta ...
- 【BZOJ2754】喵星球上的点名(AC自动机)
[BZOJ2754]喵星球上的点名(AC自动机) 题面 BZOJ 题解 友情提示:此题请不要在cogs上提交,它的数据有毒 对于点名串构建\(AC\)自动机 然后把名字丢进去进行匹配, 大力统计一下答 ...
随机推荐
- torch.nn.CrossEntropyLoss
class torch.nn.CrossEntropyLoss(weight=None, size_average=True, ignore_index=-100, reduce=True) 我这里没 ...
- 【黑客免杀攻防】读书笔记15 - 源码免杀、C++壳的编写
1.源码免杀 1.1 定位产生特征的源码 定位文件特征 1.根据MyCCL的特征码定位工具,定位出有特征的地址 2.根据VS的反汇编窗口,输入有特征的地址得到特征地址与源码的关系 3.插入Messag ...
- 内核定时器的使用(好几个例子add_timer)【转】
转自:http://blog.csdn.net/jidonghui/article/details/7449546 LINUX内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个 ...
- 按需引入antd报错问题
1.使用create-react-app工具创建了一个项目 create-react-app antd-demo 2.安装babel-plugin-import npm install babel-p ...
- Python生成器-博文读后感
Windows 10家庭中文版,Python 3.6.4, 上午看过了一篇讲Python生成器的博文: 提高你的Python: 解释‘yield’和‘Generators(生成器)’(英文原文) 这篇 ...
- jersey HTTP Status 400 - Bad Request
原因是jersey 内置的转换器,只能做简单的类型转换如: 首先客户端提交上来的一定是String; String ----> String/Long/Boolean 这些基本的 可以转换,但是 ...
- 最简单删除SQL Server中所有数据的方法(不用考虑表之间的约束条件,即主表与子表的关系)
其实删除数据库中数据的方法并不复杂,为什么我还要多此一举呢,一是我这里介绍的是删除数据库的所有数据,因为数据之间可能形成相互约束关系,删除操作可能陷入死循环,二是这里使用了微软未正式公开的sp_MSF ...
- R语言学习笔记:sort、rank、order、arrange排序函数
R语言中排序有几个基本函数:sort().rank().order().arrange() 一.总结 sort()函数是对向量进行从小到大的排序 rank()函数返回的是对向量中每个数值对应的秩 or ...
- 在windows中安装两个不同版本的Python
这段时间买了一本 利用Python进行数据分析的书.书上要我将原来安装的Python的环境去掉,但是我觉得这样做不行,我以前写过的很多东西还在呢.遂在博客中找到了解决方法,记录之. 首先,我们安装了两 ...
- Fix Valgrind's must-be-redirected error in Gentoo
Last week, I tried to use Valgrind to identify potential memory related bugs, since segmentation fau ...