zoukankan      html  css  js  c++  java
  • 【BZOJ 2754】[SCOI2012]喵星球上的点名

    【链接】h在这里写链接


    【题意】


        n个人;
        由姓和名组成。s1[i]和s2[i];
        有m个询问串。
        问你第j个询问串,是否为某个人的姓或者名的子串。
        如果是的话,那个人就要答到。
        每个询问串,问有多少个人答到。
        最后再输出一行,每个人分别答到 多少次。


    【题解】


        后缀数组题。
        先把所给的2*n个字符串,用东西隔开(名和姓也要隔开)。
        (记录该下标对应的人是谁)
        造成一个字符串s。
        然后把所有的询问串也用间隔符加在s后面。
        (询问对应的人记录成0就好)
        (记录每个询问穿的长度len[i])
        记录每个询问在字符串中开始的位置。L[i];
        然后把整个s求一下Height数组和Sa数组和Rank数组。
        然后枚举每一个询问。
        看看L[i]后缀的排名temp = Rank[L[i]];
        然后如果往左走 Height[temp]>=len[i];
        则记录对应的人是谁?(如果不是0的话)
        加入到set里面。
        然后如果往右走 Height[temp+1]>=len[i]
        则记录对应的人是谁。
        加入到set里面
        然后输出set的大小。
        以及把每个set里的家伙的达到次数递增。


    【错的次数】


    0

    【反思】


    BZOJ不能用cin,cout...

    【代码】

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, m;
    
    const int N = 4e5;//有分隔符
    const int M = 5e4;
    const int MAX_CHAR = 100000+100;//每个数字的最大值。因为有分隔符。
    int s[N + 10];//如果是数字,就写成int s[N+10]就好,从0开始存
    int Sa[N + 10], T1[N + 10], T2[N + 10], C[N + 10], idx[N + 10];
    int Height[N + 10], Rank[N + 10], L[N + 10], ans[N + 10];
    int lenq[M + 10];
    
    void build_Sa(int n, int m) {
    	int i, *x = T1, *y = T2;
    	for (i = 0; i<m; i++) C[i] = 0;
    	for (i = 0; i<n; i++) C[x[i] = s[i]]++;
    	for (i = 1; i<m; i++) C[i] += C[i - 1];
    	for (i = n - 1; i >= 0; i--) Sa[--C[x[i]]] = i;
    	for (int k = 1; k <= n; k <<= 1)
    	{
    		int p = 0;
    		for (i = n - k; i<n; i++) y[p++] = i;
    		for (i = 0; i<n; i++) if (Sa[i] >= k) y[p++] = Sa[i] - k;
    		for (i = 0; i<m; i++) C[i] = 0;
    		for (i = 0; i<n; i++) C[x[y[i]]]++;
    		for (i = 1; i<m; i++) C[i] += C[i - 1];
    		for (i = n - 1; i >= 0; i--) Sa[--C[x[y[i]]]] = y[i];
    		swap(x, y);
    		p = 1; x[Sa[0]] = 0;
    		for (i = 1; i<n; i++)
    			x[Sa[i]] = y[Sa[i - 1]] == y[Sa[i]] && y[Sa[i - 1] + k] == y[Sa[i] + k] ? p - 1 : p++;
    		if (p >= n) break;
    		m = p;
    	}
    }
    
    void getHeight(int n)
    {
    	int i, j, k = 0;
    	for (i = 1; i <= n; i++) Rank[Sa[i]] = i;
    	for (i = 0; i<n; i++) {
    		if (k) k--;
    		j = Sa[Rank[i] - 1];
    		while (s[i + k] == s[j + k]) k++;
    		Height[Rank[i]] = k;
    	}
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    	scanf("%d%d", &n, &m);
    	int len = 0, spe = 0;
    	for (int i = 1; i <= n; i++)
    	{//输入n个人的姓和名
    		int num;
    		scanf("%d", &num);
    		for (int j = 1; j <= num; j++)//输入姓
    		{
    			int x;
    			scanf("%d", &x);
    			idx[len] = i;
    			s[len++] = x + 1;
    		}
    		spe++;
    		s[len++] = spe + 10005;
    		scanf("%d", &num);
    		for (int j = 1; j <= num; j++)//输入名
    		{
    			int x;
    			scanf("%d", &x);
    			idx[len] = i;//记录这个人是谁
    			s[len++] = x + 1;
    		}
    		spe++;
    		s[len++] = spe + 10005;
    	}
    
    	for (int i = 1; i <= m; i++) {
    		scanf("%d", &lenq[i]);
    		L[i] = len;
    		for (int j = 1; j <= lenq[i]; j++) {
    			int x;
    			scanf("%d", &x);
    			s[len++] = x + 1;
    		}
    		spe++;
    		s[len++] = spe + 10005;
    	}
    
    	s[len] = 0;
    	build_Sa(len + 1, MAX_CHAR);//注意调用n+1
    	getHeight(len);
    
    	for (int i = 1; i <= m; i++) {//枚举每一个询问
    		set <int> mset;
    		mset.clear();
    		int temp = Rank[L[i]];
    		while (temp - 1 >= 1 && Height[temp] >= lenq[i]) {
    			temp--;
    			int k = Sa[temp];//排名第temp的后缀,是从何处开始的
    			if (idx[k] != 0)  mset.insert(idx[k]);//如果是一个人的名字
    		}
    		temp = Rank[L[i]];
    		while (temp + 1 <= len && Height[temp + 1] >= lenq[i]) {
    			temp++;
    			int k = Sa[temp];//排名第temp的后缀,从何处开始的
    			if (idx[k] != 0) mset.insert(idx[k]);
    		}
    		printf("%d
    ", (int)mset.size());
    		set<int>::iterator it;
    		for (it = mset.begin(); it != mset.end(); it++) {
    			ans[(*it)]++;
    		}
    	}
    
    	for (int i = 1; i <= n; i++)
    		if (i == n)
    			printf("%d
    ", ans[i]);
    		else
    			printf("%d ", ans[i]);
    	return 0;
    }


  • 相关阅读:
    NOIP2011 D1T1 铺地毯
    NOIP2013 D1T3 货车运输 倍增LCA OR 并查集按秩合并
    POJ 2513 trie树+并查集判断无向图的欧拉路
    599. Minimum Index Sum of Two Lists
    594. Longest Harmonious Subsequence
    575. Distribute Candies
    554. Brick Wall
    535. Encode and Decode TinyURL(rand and srand)
    525. Contiguous Array
    500. Keyboard Row
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7625996.html
Copyright © 2011-2022 走看看