• bzoj2754


    看到这道题一开始想到的是后缀数组+二分+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
  • 相关阅读:
    c++ 两个set合并
    L2-2 小字辈 (25 分)
    L1-1 天梯赛座位分配
    c++ 用 0x3f3f3f3f 设定最大int值的优点
    Treap(树堆)(转)
    new一个二维数组(转)
    Laplacian matrix(转)
    寒假计划制定
    寒假集训日志(八,九,十)——浪浪浪
    寒假集训日志(七)——数据结构
  • 原文地址:https://www.cnblogs.com/phile/p/4473190.html
Copyright © 2020-2023  润新知