jzoj 2747.【2012中山市选】捡金子(gold) 字典树orhash+树形dp

26 篇文章 0 订阅

Description

从前有一个迷宫,迷宫的外形就像一棵带根树,每个结点(除了叶子结点外)恰好有K个儿子。

一开始你在根结点,根结点的K个儿子分别标记为‘A’, ‘B’, ‘C’….,而结点‘A’的K个儿子结点分别标记为‘AA’,‘AB’,‘AC’……,依此类推。这棵树一共有L层。

现在你事先知道M个结点中有金子,并且你可以派出N个机器人去收集金子。首先你可以分别指定每一个机器人的目标结点,于是这些机器人就会收集从根结点到其目标结点这条路径上(包括目标结点)所有的金子,但是每个位置的金子只能被收集一次。

现在你需要制定一个目标的分配方案,使得收集到的金子最多。

Input

输入的第一行有4个整数:M,K,L,N。对应题目描述中的参数。

接下来M行,每行是一个字符串,表示所对应的结点上有金子。

Output

输出利用N个机器人所能捡到时最多几个结点上的金子。

Sample Input

样例1:
5 3 3 1
ACC
ACB
AB
AC
A

样例2:
5 3 3 2
ACC
ACB
AB
AC
A

Sample Output

样例输出1:
3

样例输出2:
4

Data Constraint

Hint

对于20%的数据有1<=M<=20.

对于40%的数据有1<=M<=2000.

对100%的数据,有
1<=M<=50000,1<=K<=26,1<=L<=50,1<=N<=50

分析:要取到一个字符串,可以把该字符串前缀全部取到。所以对于每个有金子字符串,向它的有金子的最长前缀连一条边。找前缀可以用排序后+hash或者字典树。
如:
S1=’AA’
S2=’AAA’
S3=’AAAB’
于是3向2连边,2向1连边,3不必向1连边。从而构成一个树的结构。
我们考虑树上统计答案,一个很显然的想法就是,设f[x,i]为到x节点,用了i个机器人的答案,有
f[x,i]=max(f[x,i],f[x,i-j],f[x.son,j])
复杂度为O(MN^2)
一开始以为会被卡,然后就过了= =

代码:

const
 p=700007;
 maxn=50001;
type
 node=record
  y,next:longint;
 end;
 re=record
  s:string;
  x:longint;
 end;

var
 n,m,k,l,x,e,i,j:longint;
 hash:array [0..p] of re;
 g:array [1..maxn*3] of node;
 ls,a:array [0..maxn] of longint;
 f:array [0..maxn,0..51] of longint;
 v:array [0..maxn] of boolean;
 s:array [0..maxn] of string;
 flag:boolean;

function h(s:string):longint;
 var i,pow,c:longint;
begin
 pow:=1; c:=0;
 for i:=1 to length(s) do
  begin
   c:=(c+pow*(ord(s[i])-ord('A'))) mod p;
   pow:=(pow*26) mod p;
  end;
 exit(c);
end;

procedure ins(s:string;x:longint);
 var i:longint;
begin
 i:=h(s);
 while hash[i].s<>'' do
  i:=i mod p+1;
 hash[i].s:=s;
 hash[i].x:=x;
end;

function find(s:string):longint;
 var i:longint;
begin
 i:=h(s);
 while hash[i].s<>'' do
  begin
   if hash[i].s=s then exit(hash[i].x);
   i:=i mod p+1;
  end;
 exit(0);
end;

procedure add(x,y:longint);
 begin
  inc(e);
  g[e].y:=y;
  g[e].next:=ls[x];
  ls[x]:=e;
 end;

function max(x,y:longint):longint;
 begin
  if x>y then exit(x)
         else exit(y);
 end;

procedure qsort(l,r:longint);
  var
    i,j:longint;
    key,temp:string;
  begin
    if l>=r then exit;
    i:=l;j:=r;
    key:=s[l+random(r-l+1)];
    repeat
      while  (s[i]<key) do inc(i);
      while  (s[j]>key) do dec(j);
      if i<=j then
      begin
        temp:=s[i];s[i]:=s[j];s[j]:=temp;
        inc(i);dec(j);
      end;
    until i>j;
    qsort(l,j);
    qsort(i,r);
  end;

procedure dfs(x:longint);
 var t,i,j:longint;
begin
 f[x,0]:=0;
 t:=ls[x];
 while t>0 do
  with g[t] do
   begin
    if v[y]=false then
     begin
      v[y]:=true;
      dfs(y);
      for i:=n downto 1 do
       for j:=0 to i do
        f[x,i]:=max(f[x,i],f[x,i-j]+f[y,j]);
     end;
    t:=next;
   end;
 for i:=1 to n do
  f[x,i]:=f[x,i]+a[x];
end;

begin
 readln(m,k,l,n);
 for i:=1 to m do
  readln(s[i]);
 qsort(1,m);
 for i:=1 to m do
  begin
   flag:=false;
   for j:=length(s[i]) downto 1 do
    begin
     x:=find(copy(s[i],1,j));
     if x<>0 then
      begin
       flag:=true;
       if j=length(s[i]) then inc(a[x])
        else
         begin
          add(x,i);
          add(i,x);
          ins(s[i],i);
          a[i]:=1;
         end;
       break;
      end;
    end;
   if flag=false then begin add(0,i); ins(s[i],i); a[i]:=1; end;
  end;
 v[0]:=true;
 dfs(0);
 writeln(f[0,n]);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值