1. 程式人生 > >BZOJ 2754 【SCOI2012】 喵星球上的點名

BZOJ 2754 【SCOI2012】 喵星球上的點名

iostream 節點 php string namespace mat blog void 關鍵字排序

題目鏈接:喵星球上的點名

  首先可以發現姓和名兩個串就是逗你玩的。在兩個串中間插入一個\(10001\),當成一個串做就可以了。

  於是我們的問題轉化為了:

  有\(n\)個串\(A_1,A_2,\dots,A_n\)和\(m\)個串\(B_1,B_2,\dots,B_m\),要對於每個\(B_i\)求出它被多少個\(A\)串包含,並要對每個\(A_i\)求出它包含了多少個\(B\)串。

  我們先把所有串丟到一個\(AC\)自動機裏面,然後構出\(fail\)樹。我們知道,如果\(S\)串包含了\(A\)串,那麽在\(fail\)樹上\(A\)串的結尾節點的子樹裏就會有\(S\)串的節點。所以構出\(dfs\)序之後,要求每個\(B_i\)求出它被多少個\(A\)串包含,就相當於詢問區間不同的顏色數。做法同HH的項鏈。

  第二問就是相當於每次給區間內所有元素加\(1\),但是相同的元素只加一次。記位置\(i\)的元素上一次出現的位置為\(pre_i\),那麽每次操作\((l,r)\)就相當於給其中滿足\(pre_i<l \le i \le r\)的\(i\)位置的元素加\(1\)。我們把所有二元組\(pre_i,i\)和所有詢問\(l,r\)都按前一個數為第一關鍵字,後一個數為第二關鍵字排序,然後一起從後往前掃。對於每個二元組\(pre_i,i\),找出所有的滿足\(pre_i<l\)的\(l,r\),給\(l,r\)區間加\(1\),然後位置\(i\)上的值就是\(i\)位置的元素被統計的次數。用一個樹狀數組維護即可。

  下面貼代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<vector>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 300010
#define pb push_back

using namespace std;
typedef long long llg;

struct data{
	int l,r,b;
	bool operator < (const data &h)const{return l<h.l;}
}A[maxn],B[maxn];
int n,m,b[maxn],lb,tt,val[maxn],pr[maxn];
int d[maxn],fr[maxn],fl[maxn],a[maxn],la,c[maxn];
int hd[maxn],nt[maxn],le[maxn],ri[maxn],ans[maxn];
map<short int,int>s[maxn];
map<short int,int>::iterator it;
vector<int> q[maxn];

int getint(){
	int w=0;bool q=0;
	char c=getchar();
	while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar();
	if(c==‘-‘) c=getchar(),q=1;
	while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar();
	return q?-w:w;
}

bool cmpr(data x,data y){return x.r<y.r;}
int insert(int x){
	int u=0;
	for(int i=1;i<=lb;i++){
		if(!s[u][b[i]]) s[u][b[i]]=++tt,val[tt]=b[i];
		u=s[u][b[i]]; if(x) q[u].pb(x);
	}
	return u;
}

void getfail(){
	int ld=0,rd=0; d[rd++]=0;
	while(ld!=rd){
		int u=d[ld++],j,v;
		if(u) nt[u]=hd[fl[u]],hd[fl[u]]=u;
		for(it=s[u].begin();it!=s[u].end();it++){
			j=fl[u]; d[rd++]=v=(*it).second;
			while(!s[j][val[v]] && j) j=fl[j];
			if(u!=j) fl[v]=s[j][val[v]];
		}
	}
}

void dfs(int u){
	le[u]=la+1;
	for(int i=q[u].size()-1;i>=0;i--){
		a[++la]=q[u][i]; A[la].r=la;
		A[la].l=pr[a[la]]; pr[a[la]]=la;
	}
	for(int i=hd[u];i;i=nt[i]) dfs(i);
	ri[u]=la;
}

void add(int x,int y){while(x<=la) c[x]+=y,x+=x&(-x);}
int sum(int x){
	int t=0;
	while(x) t+=c[x],x-=x&(-x);
	return t;
}

int main(){
	File("a");
	n=getint(),m=getint();
	for(int i=1,x;i<=n;i++){
		lb=0; x=getint();
		while(x--) b[++lb]=getint();
		b[++lb]=10001; x=getint();
		while(x--) b[++lb]=getint();
		insert(i);
	}
	for(int i=1,x;i<=m;i++){
		lb=0; x=getint();
		while(x--) b[++lb]=getint();
		fr[i]=insert(0);
	}
	getfail(); dfs(0);
	for(int i=1;i<=m;i++)
		B[i].l=le[fr[i]],B[i].r=ri[fr[i]],B[i].b=i;
	sort(B+1,B+m+1,cmpr); int bl=1;
	while(!B[bl].r) bl++;
	for(int i=1;i<=la;i++) pr[a[i]]=0;
	for(int i=1,now=0;i<=la;i++){
		if(pr[a[i]]) add(pr[a[i]],-1);
		now+=(!pr[a[i]]); add(i,1); pr[a[i]]=i;
		while(B[bl].r==i) ans[B[bl].b]=now-sum(B[bl].l-1),bl++;
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]),ans[i]=0;
	for(int i=1;i<=la;i++) c[i]=0;
	sort(A+1,A+la+1); sort(B+1,B+m+1);
	for(int i=la,j=m;i;){
		if(A[i].l<B[j].l) add(B[j].l,1),add(B[j].r+1,-1),j--;
		else ans[a[A[i].r]]+=sum(A[i].r),i--;
	}
	for(int i=1;i<=n;i++){
		printf("%d",ans[i]);
		if(i<n) putchar(‘ ‘);
	}
	return 0;
}

  

BZOJ 2754 【SCOI2012】 喵星球上的點名