【uva1502/hdu4117-GRE Words】DP+线段树优化+AC自动机

这题我的代码在hdu上AC,在uva上WA。

题意:按顺序输入n个串以及它的权值di,要求在其中选取一些串,前一个必须是后一个的子串。问d值的和最大是多少。 (1≤n≤2×10^4 ,串的总长度<=3*10^5)

题解:

这题一开始我的方向就错了,想了很久d[x][y]表示在AC自动机上的节点x、下一个串要大于y的dp。然而这样做数组要10^4*10^5=10^9级别,开都开不了,妥妥超时。

后来看了一眼题解。。。觉得自己智商真是感人。。。

用f[i]表示以第i个串为结尾的时候最大的d值,这样做就可以知道在AC自动机上的位置、当前在第几个串。

f[i]=max(f[j])+w[i],j所代表的串为i的子串。

现在我们要快速知道子串:

1.

建立AC自动机,然后将fail反向建立fail树。

对于fail树上某个点来说,它的祖先所代表的串必为它的子串(通过一直fail可以到达的点)

fail树上做一遍dfs求出dfn,就可以做到线性时间维护子树的最值。

2.

字符串x在fail树上求到的子串是除了x本身、在其他字符串中求到的子串,但是x自己的某一段也是自己的子串。所以在AC自动机上走到x的末尾节点,所经过的路程每一个点所代表的字符串也是x的子串。

对于每一个i,我把已求出来的f[i]放到failtree上它所对应的点,然后i的整个子树的最大值都要更新一遍。所以我们要开一棵线段树来维护,用failtree的dfn来作为线段树的下标,维护区间最大值。

求f[i]的时候就在AC自动机上走一遍字符串i,对于每个经过的点询问一遍它的值-------解决了第二种子串

询问i的末尾节点的值-----解决了第一种子串,因为在i前面、failtree上是i的祖先的点已经更新了i。

然后取最值,放入failtree上,更新failtree的子树即可。

  1 //hdu4117
  2
  3 #include<cstdio>
  4 #include<cstdlib>
  5 #include<cstring>
  6 #include<iostream>
  7 #include<queue>
  8 using namespace std;
  9
 10 const int N=300010,M=20010,S=26;
 11 int n,num,cnt_dfn,cnt_seg,len,w[M],last[M],first[N],dfn[N],next_dfn[N];
 12 char s[N];
 13 struct trie_node{
 14     int fa,fail,son[30];
 15 }a[N];
 16 struct fail_node{
 17     int x,y,next;
 18 }b[N];
 19 struct seg_node{
 20     int lc,rc,l,r,d,lazy;
 21 }t[2*N];
 22 queue<int> q;
 23
 24 int maxx(int x,int y){return x>y ? x:y;}
 25
 26 void clear(int x)
 27 {
 28     a[x].fail=a[x].fa=0;
 29     memset(a[x].son,0,sizeof(a[x].son));
 30 }
 31
 32 void ins(int x,int y)
 33 {
 34     b[++len].x=x;b[len].y=y;
 35     b[len].next=first[x];first[x]=len;
 36 }
 37
 38 void read_trie(int id)
 39 {
 40     scanf("%s%d",s,&w[id]);
 41     int x=0,l=strlen(s);
 42     for(int i=0;i<l;i++)
 43     {
 44         // if(!(s[i]>=‘a‘ && s[i]<=‘z‘)) while(1) ;
 45         int ind=s[i]-‘a‘+1;
 46         if(!a[x].son[ind])
 47         {
 48             num++;
 49             clear(num);
 50             a[x].son[ind]=num;
 51             a[num].fa=x;
 52         }
 53         x=a[x].son[ind];
 54     }
 55     last[id]=x;
 56 }
 57
 58 void build_AC_failtree()
 59 {
 60     while(!q.empty()) q.pop();
 61     for(int i=1;i<=S;i++)
 62         if(a[0].son[i]) q.push(a[0].son[i]);
 63     while(!q.empty())
 64     {
 65         int x=q.front();q.pop();
 66         int fail=a[x].fail;
 67         for(int i=1;i<=S;i++)
 68         {
 69             if(a[x].son[i])
 70             {
 71                 a[a[x].son[i]].fail=a[fail].son[i];
 72                 q.push(a[x].son[i]);
 73             }
 74             else a[x].son[i]=a[fail].son[i];
 75         }
 76     }
 77     for(int i=1;i<=num;i++)
 78         ins(a[i].fail,i);
 79 }
 80
 81 void make_dfn(int x)
 82 {
 83     dfn[x]=++cnt_dfn;
 84     for(int i=first[x];i;i=b[i].next) make_dfn(b[i].y);
 85     next_dfn[x]=cnt_dfn;
 86 }
 87
 88 int build_segtree(int l,int r)
 89 {
 90     cnt_seg++;
 91     int x=cnt_seg;
 92     t[x].l=l;t[x].r=r;t[x].d=0;
 93     t[x].lc=t[x].rc=0;t[x].lazy=0;//debug 原本根节点是0,但是这里也是0,就WA了
 94     if(l!=r)
 95     {
 96         int mid=(l+r)>>1;
 97         t[x].lc=build_segtree(l,mid);
 98         t[x].rc=build_segtree(mid+1,r);
 99     }
100     return x;
101 }
102
103 void updata(int x)
104 {
105     if(!t[x].lazy) return;
106     int lazy=t[x].lazy,lc=t[x].lc,rc=t[x].rc;
107     t[x].d=maxx(t[x].d,lazy);
108     t[x].lazy=0;
109     t[lc].lazy=maxx(t[lc].lazy,lazy);//debug****
110     t[rc].lazy=maxx(t[rc].lazy,lazy);//debug****
111 }
112
113 void change(int x,int l,int r,int d)
114 {
115     updata(x);
116     if(t[x].l==l && t[x].r==r) {t[x].lazy=d;return;}
117     int lc=t[x].lc,rc=t[x].rc;
118     int mid=(t[x].l+t[x].r)>>1;
119     if(r<=mid) change(lc,l,r,d);
120     else if(l>mid) change(rc,l,r,d);
121     else
122     {
123         change(lc,l,mid,d);
124         change(rc,mid+1,r,d);
125     }
126     updata(lc);
127     updata(rc);
128     t[x].d=maxx(t[lc].d,t[rc].d);
129 }
130
131 int query(int x,int y)
132 {
133     if(t[x].lazy) updata(x);
134     if(t[x].l==t[x].r) return t[x].d;
135     int lc=t[x].lc,rc=t[x].rc;
136     int mid=(t[x].l+t[x].r)>>1;
137     if(y<=mid) return query(lc,y);
138     if(y>mid) return query(rc,y);
139 }
140
141 int dp_AC(int x,int now,int id)
142 {
143     if(x==0) return now;
144     return dp_AC(a[x].fa,maxx(now,query(1,dfn[x])),id);
145 }
146
147 int main()
148 {
149     //freopen("a.in","r",stdin);
150     //freopen("a.out","w",stdout);
151     int T,TT=0;
152     scanf("%d",&T);
153     while(T--)
154     {
155         scanf("%d",&n);
156         num=0;len=0;cnt_dfn=-1;cnt_seg=0;//原本cnt_seg=-1,根节点=0,后来cnt_seg改成0,根节点=1
157         clear(0);
158         memset(first,0,sizeof(first));
159         for(int i=1;i<=n;i++) read_trie(i);
160         build_AC_failtree();
161         make_dfn(0);
162         build_segtree(1,num);
163         //dp
164         int mx=0,x,fx;
165         for(int i=1;i<=n;i++)
166         {
167             x=last[i];
168             fx=dp_AC(x,0,i)+w[i];
169             change(1,dfn[x],next_dfn[x],fx);
170             mx=maxx(mx,fx);
171         }
172         printf("Case #%d: %d\n",++TT,mx);
173     }
174 }
时间: 2024-10-17 14:39:48

【uva1502/hdu4117-GRE Words】DP+线段树优化+AC自动机的相关文章

题解 HDU 3698 Let the light guide us Dp + 线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 253 Problem Description Plain of despair was

HDU4719-Oh My Holy FFF(DP线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 606    Accepted Submission(s): 141 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

hdu 4719 Oh My Holy FFF(dp线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 848    Accepted Submission(s): 219 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

hdu 3450 离散化+dp+线段树优化

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3450 题意: 给你一串长度为n的序列,给一个d,要求找出有几个子序列能够满足两个相邻的元素之间差值不超过d. 思路: dp.定义dp[i]表示以第i个为结束的满足条件的子序列的个数. 转移方程:dp[i]=(∑i?1j=1dp[j])+1(abs(num[i]?num[j])<=d) 答案就是dp数组的总和最后扣掉n就可以了. 此时会发现更新的时间复杂度是O(n2),这个显然是过不了的. 转移的复杂度是

hdu3698 Let the light guide us dp+线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 821    Accepted Submission(s): 285 Problem Description Plain of despair was

ZOJ 3650(多米诺骨牌 dp + 线段树优化)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3650 题意: 给你n个骨牌,每个骨牌有一个在x轴上的位置和高度,每个骨牌可以想左推也可以向右推,问最少多少步可以把骨牌全部推倒. 思路: 之前Goodbye 2014 上的E题就是一道多米诺骨牌的题目,虽然跟这道题目不太一样但是还是提供了一下思路, 附题解的链接: http://blog.csdn.net/u013649253/article/details/4

[后缀数组+dp/AC自动机+dp+线段树] hdu 4117 GRE Words

题意: 给你N个字符串, N(1 <= N <= 2w), 所有串的长度加一起不超过30w.每个串有个值.这个值[-1000, 1000]. 问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值. 思路: 其实就是一个很明显的dp. dp[i]代表以第i个字符串结尾的最大权值. 但是就是子串这个问题怎么处理. 由于这题数据比较水可以用后缀数组处理这个问题. 将所有字符串拼接,做sa. 每次在height数组里往上和往下寻找公共前缀等

[Poi2010]Monotonicity 2 (线段树优化DP)

题目描述 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k].选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1].求出L的最大值. 输入 第一行两个正整数,分别表示N和K (N, K <= 500,000).第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6).第三行给出K个空格隔开关系符号(>.<或=),第i个表示s[i]. 输出 一个正整数,表示L的

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=