看到这道题一开始想到的是后缀数组+二分+rmq

类似bzoj3172

问每个串i在合并后的串出现了多少次

等价于有多少个后缀j,使得LCP(i,j)>=length(s[i])

但是想想又不对,要求求的是有多少人被点到,每个人点到多少次

可能有多个后缀j满足条件但其实都是一个人的名字的一部分

好像二分搞不动,只能顺着名次依次找,理论上极其极端的数据是可以卡掉

但是实际却过了,,内疚啊……

UPD:太神了,这题有非暴力的做法,orz http://oi.nks.edu.cn/showmessage?message_id=4091

  1 const inf=10010;
  2 var s,sum,be,h,x,y,rank,sa:array[0..400010] of longint;
  3     q,len,w:array[0..50010] of longint;
  4     v:array[0..50010] of boolean;
  5     tot,c,p,m,n,t,l,i,j:longint;
  6 
  7 function min(a,b:longint):longint;
  8   begin
  9     if a>b then exit(b) else exit(a);
 10   end;
 11 
 12 procedure suffix(n:longint);
 13   var m:longint;
 14   begin
 15     for i:=1 to t do
 16       inc(sum[s[i]]);
 17     m:=inf;
 18     for i:=1 to m do
 19       inc(sum[i],sum[i-1]);
 20     for i:=n downto  1 do
 21     begin
 22       sa[sum[s[i]]]:=i;
 23       dec(sum[s[i]]);
 24     end;
 25     p:=1;
 26     rank[sa[1]]:=1;
 27     for i:=2 to n do
 28     begin
 29       if (s[sa[i]]<>s[sa[i-1]]) then inc(p);
 30       rank[sa[i]]:=p;
 31     end;
 32     m:=p;
 33     j:=1;
 34     while m<n do
 35     begin
 36       fillchar(sum,sizeof(sum),0);
 37       y:=rank;
 38       p:=0;
 39       for i:=n-j+1 to n do
 40       begin
 41         inc(p);
 42         x[p]:=i;
 43       end;
 44       for i:=1 to n do
 45         if sa[i]>j then
 46         begin
 47           inc(p);
 48           x[p]:=sa[i]-j;
 49         end;
 50 
 51       for i:=1 to n do
 52       begin
 53         rank[i]:=y[x[i]];
 54         inc(sum[rank[i]]);
 55       end;
 56       for i:=1 to m do
 57         inc(sum[i],sum[i-1]);
 58       for i:=n downto 1 do
 59       begin
 60         sa[sum[rank[i]]]:=x[i];
 61         dec(sum[rank[i]]);
 62       end;
 63       p:=1;
 64       rank[sa[1]]:=1;
 65       for i:=2 to n do
 66       begin
 67         if (y[sa[i]]<>y[sa[i-1]]) or (y[sa[i]+j]<>y[sa[i-1]+j]) then inc(p);
 68         rank[sa[i]]:=p;
 69       end;
 70       m:=p;
 71       j:=j shl 1;
 72     end;
 73     h[1]:=0;
 74     p:=0;
 75     for i:=1 to n do
 76     begin
 77       if rank[i]=1 then continue;
 78       j:=sa[rank[i]-1];
 79       while s[i+p]=s[j+p] do inc(p);
 80       h[rank[i]]:=p;
 81       if p>0 then dec(p);
 82     end;
 83   end;
 84 
 85 begin
 86   readln(n,m);
 87   for i:=1 to n do
 88   begin
 89     read(l);
 90     for j:=1 to l do
 91     begin
 92       inc(t);
 93       read(s[t]);
 94       be[t]:=i;
 95     end;
 96     inc(t);
 97     s[t]:=inf;  //注意姓和名之间也要加分隔符,防止点名串一部分在姓,一部分在名的情况
 98     read(l);
 99     for j:=1 to l do
100     begin
101       inc(t);
102       read(s[t]);
103       be[t]:=i;
104     end;
105     inc(t);
106     s[t]:=inf;
107   end;
108   for i:=1 to m do
109   begin
110     read(len[i]);
111     w[i]:=t+1;
112     for j:=1 to len[i] do
113     begin
114       inc(t);
115       read(s[t]);
116       be[t]:=i+n;
117     end;
118     inc(t);
119     s[t]:=inf;
120   end;
121   suffix(t);
122   fillchar(sum,sizeof(sum),0);
123   tot:=0;
124   for i:=1 to m do
125   begin
126     for j:=1 to tot do  //小小优化
127       v[q[j]]:=false;
128     tot:=0;
129     j:=rank[w[i]];
130     l:=2147483647;
131     while j<=t do      //找名次比点名串大的后缀
132     begin
133       inc(j);
134       c:=sa[j];
135       l:=min(h[j],l);    //height数组和LCP的关系
136       if l<len[i] then break
137       else begin
138         if (be[c]>=1) and (be[c]<=n) and not v[be[c]] then
139         begin
140           v[be[c]]:=true;  //不能重复统计
141           inc(tot);
142           q[tot]:=be[c];
143           inc(sum[be[c]]);
144         end;
145       end;
146     end;
147     j:=rank[w[i]];
148     l:=h[j];
149     while j>0 do     //找名次比点名串小的后缀
150     begin
151       dec(j);
152       c:=sa[j];
153       if l<len[i] then break
154       else begin
155         if (be[c]>=1) and (be[c]<=n) and not v[be[c]] then
156         begin
157           v[be[c]]:=true;
158           inc(tot);
159           q[tot]:=be[c];
160           inc(sum[be[c]]);
161         end;
162       end;
163       l:=min(l,h[j]);
164     end;
165     writeln(tot);
166   end;
167   for i:=1 to n do
168   begin
169     write(sum[i]);
170     if i<>n then write(' ');
171   end;
172   writeln;
173 end.
View Code

 

posted on 2014-07-24 15:28  acphile  阅读(168)  评论(0编辑  收藏  举报