天天看點

SCOI 2012 喵星球上的點名 字尾數組+莫隊

Description

a180285幸運地被選做了地球到喵星球的留學生。他發現喵星人在上課前的點名現象非常有趣。 假設課堂上有N個喵星人,每個喵星人的名字由姓和名構成。喵星球上的老師會選擇M個串來點名,每次讀出一個串的時候,如果這個串是一個喵星人的姓或名的子串,那麼這個喵星人就必須答到。 然而,由于喵星人的字碼過于古怪,以至于不能用ASCII碼來表示。為了友善描述,a180285決定用數串來表示喵星人的名字。

現在你能幫助a180285統計每次點名的時候有多少喵星人答到,以及M次點名結束後每個喵星人答到多少次嗎?

Input

現在定義喵星球上的字元串給定方法:

先給出一個正整數L,表示字元串的長度,接下來L個整數表示字元串的每個字元。

輸入的第一行是兩個整數N和M。

接下來有N行,每行包含第i 個喵星人的姓和名兩個串。姓和名都是标準的喵星球上的

字元串。

接下來有M行,每行包含一個喵星球上的字元串,表示老師點名的串。

Output

對于每個老師點名的串輸出有多少個喵星人應該答到。

然後在最後一行輸出每個喵星人被點到多少次。

思路:

本來是個AC自動機裸題,但是由于字元集很大,是以AC自動機要套個map,比較慢(不過好像可以過)。

于是就作死地寫了個字尾數組。把所有字元串丢進一個母串跑字尾數組,用10001隔開。對于每個模式串(即老師點名的串),我們可以二分出它在字尾數組中的比對區間(即與某個排名區間内的字尾的LCP大于等于它的自身串長)。

接下來考慮第一問,這相當于查詢每個模式串的比對區間内有多少個喵星人,他們姓名的某一個字尾在該區間内出現過。莫隊一發即可。

對于第二問,我們不妨對于每一個姓名的字尾單獨計算其貢獻,但是需要減掉重複的部分。我們可以将每個比對區間的左右端點排序,然後按排名掃一遍字尾數組,同時對于每一個姓名記錄其字尾上一次出現的位置。若遇到一個左端點,以其左端點為關鍵字加入樹狀數組;碰到一個右端點則将開始加入的左端點删掉。若該位置是一個姓名的字尾,隻需将這個姓名的答案加上目前在樹狀數組中的左端點個數,再減去上一次已經算了的部分。

SCOI 2012 喵星球上的點名 字尾數組+莫隊

為總串長,則時間複雜度為

SCOI 2012 喵星球上的點名 字尾數組+莫隊

,事實上比

SCOI 2012 喵星球上的點名 字尾數組+莫隊

的暴力還慢。。。

驚奇地發現這是我至今寫過的最長的資料結構題?

代碼:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>

#define For(i, j, k) for(int i = j; i <= k; i++)
#define Forr(i, j, k) for(int i = j; i >= k; i--)
#define getchar getchar_unlocked

using namespace std;

const int N = 300010;
const int MaxC = 10001;

int Read(){
	char c = getchar();
	while(c > '9' || c < '0') c = getchar();
	int x = 0;
	while(c <= '9' && c >= '0') x = x * 10 + c - '0', c = getchar();
	return x;
}

int sa[N], c[N], t1[N], t2[N], S[N], n;

void buildsa(int m){
	int *x = t1, *y = t2;
	For(i, 1, n) c[x[i] = S[i]]++;
	For(i, 1, m) c[i] += c[i - 1];
	Forr(i, n, 1) sa[c[x[i]]--] = i;
	for(int k = 1; k < n; k <<= 1){
		int p = 0;
		For(i, n - k + 1, n) y[++p] = i;
		For(i, 1, n) if(sa[i] > k) y[++p] = sa[i] - k;
		For(i, 0, m) c[i] = 0;
		For(i, 1, n) c[x[y[i]]]++;
		For(i, 1, m) c[i] += c[i - 1];
		Forr(i, n, 1) sa[c[x[y[i]]]--] = y[i];
		swap(x, y);
		x[sa[1]] = p = 1;
		For(i, 2, n) x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++p;
		if(p == n) break;
		m = p;
	}
}

int h[N][20], rk[N], Log2[N];

void buildheight(){
	For(i, 1, n) rk[sa[i]] = i;
	int j = 0;
	For(pos, 1, n){
		if(j) --j;
		int i = rk[pos];
		while(sa[i] + j <= n && sa[i + 1] + j <= n && S[sa[i] + j] == S[sa[i + 1] + j]) ++j;
		h[i][0] = j;
	}
	For(k, 1, 18)
		for(int i = 1; i + (1 << k) - 1 <= n; i++) 
			h[i][k] = min(h[i][k - 1], h[i + (1 << (k - 1))][k - 1]);
	For(i, 2, n) Log2[i] = Log2[i >> 1] + 1;
}

int query(int x, int y){
	if(x == y) return n;
	int t = Log2[y - x];
	return min(h[x][t], h[y - (1 << t)][t]);
}

int id[N];

int AnsS[N], Sn;
int bgn[N], len[N], lft[N], rt[N], AnsP[N], Pn;

void calc_interval(){
	For(i, 1, Pn){
		int x = rk[bgn[i]];
		int L = 1, R = x;
		while(L < R){
			int M = (L + R) >> 1;
			if(query(M, x) >= len[i]) R = M;
			else L = M + 1;
		}
		lft[i] = L;
		L = x, R = n;
		while(L < R){
			int M = (L + R + 1) >> 1;
			if(query(x, M) >= len[i]) L = M;
			else R = M - 1;
		}
		rt[i] = L;
	}
}

namespace questionA{
	
	int sz, Ans[N];

	struct Op{
		int l, r, id;
		
		bool operator < (const Op& B) const{
			return l / sz < B.l / sz || (l / sz == B.l / sz && r < B.r);
		}
	}A[N];

	int cnt[N], ret;

	inline void add(int x){
		if(x){
			if(!cnt[x]) ++ret;
			cnt[x]++;
		}
	}

	inline void del(int x){
		if(x){
			cnt[x]--;
			if(!cnt[x]) --ret;
		}
	}

	void main(){
		For(i, 1, Pn) A[i] = (Op){lft[i], rt[i], i};
		sz = ceil(sqrt(n));
		sort(A + 1, A + Pn + 1);
		int l = A[1].l, r = A[1].r;
		For(i, l, r) add(id[sa[i]]);
		Ans[A[1].id] = ret;
		For(i, 2, Pn){
			while(l < A[i].l) del(id[sa[l]]), ++l;
			while(l > A[i].l) --l, add(id[sa[l]]);
			while(r < A[i].r) ++r, add(id[sa[r]]);
			while(r > A[i].r) del(id[sa[r]]), --r;
			Ans[A[i].id] = ret;
		}
		For(i, 1, Pn) printf("%d\n", Ans[i]);
	}
};

namespace questionB{
	
	int c[N];

	inline int lowbit(int x){
		return x & (-x);
	}

	void add(int x, int v){
		while(x < n){
			c[x] += v;
			x += lowbit(x);
		}
	}

	int sum(int x){
		int ret = 0;
		while(x){
			ret += c[x];
			x -= lowbit(x);
		}
		return ret;
	}

	int Next[N], Ans[N];

	struct Op{
		int l, pos, v;
		
		bool operator < (const Op& B) const{
			return pos < B.pos;
		}
	}A[N];

	void main(){
		int tot = 0, p = 1;
		For(i, 1, Pn){
			A[++tot] = (Op){lft[i], lft[i], 1};
			A[++tot] = (Op){lft[i], rt[i] + 1, -1};
		}
		sort(A + 1, A + tot + 1);
		For(i, 1, n){
			while(p <= tot && A[p].pos == i) add(A[p].l, A[p].v), ++p;
			if(id[sa[i]]){
				int &x = Next[id[sa[i]]];
				Ans[id[sa[i]]] += sum(i) - sum(x);
				x = i;
			}
		}
		For(i, 1, Sn) printf("%d%c", Ans[i], i == Sn ? '\n' : ' ');
	}
};

int main(){
	Sn = Read(), Pn = Read();
	For(i, 1, Sn)
		For(j, 0, 1){
			int l = Read();
			while(l--) S[++n] = Read(), id[n] = i;
			S[++n] = MaxC;
		}
	For(i, 1, Pn){
		int l = Read();
		bgn[i] = n + 1, len[i] = l;
		while(l--) S[++n] = Read();
		S[++n] = MaxC;
	}
	buildsa(MaxC);
	buildheight();
	calc_interval();
	questionA::main();
	questionB::main();
	return 0;
}