1、bzoj1597 http://www.lydsy.com/JudgeOnline/problem.php?id=1597
题意:n块土地,现在要求把土地分成几份,每份费用为该份中土地长最大值和宽最大值成绩,要求最小费用。n≤5000
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 50100 6 #define ll long long 7 using namespace std; 8 9 struct str{ll x,y;}; str strs1[maxn],strs2[maxn]; 10 bool cmp(str a,str b){return a.x!=b.x?a.x<b.x:a.y<b.y;} 11 inline int read(){ 12 char ch=getchar(); int f=1,x=0; 13 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} 14 while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 15 return f*x; 16 } 17 int n,l,r,m,q[maxn]; ll f[maxn]; 18 double calc(int j,int k){ 19 return (double)(f[j]-f[k])/(double)(strs2[k+1].y-strs2[j+1].y); 20 } 21 int main(){ 22 n=read(); inc(i,1,n)strs1[i].x=(ll)read(),strs1[i].y=(ll)read(); sort(strs1+1,strs1+n+1,cmp); m=0; 23 inc(i,1,n){while(m&&strs2[m].y<=strs1[i].y)m--; strs2[++m]=strs1[i];} l=1; r=1; q[l]=0; 24 inc(i,1,m){ 25 while(l<r&&calc(q[l],q[l+1])<strs2[i].x)l++; f[i]=f[q[l]]+strs2[i].x*strs2[q[l]+1].y; 26 while(l<r&&calc(q[r-1],q[r])>calc(q[r],i))r--; q[++r]=i; 27 } 28 printf("%lld",f[m]); return 0; 29 }
题解:当一块土地长宽都比另一块土地小时,这块土地可以当作另一块土地的附属品,对答案不影响。因此先按长第一关键字,宽第二关键字排序,然后用单调队列就可以把长宽都被覆盖的土地除去。之后剩在单调队列里的土地长是升序排列,宽是降序排列,故用斜率优化dp:f[i]=max(f[j]+长[i]*宽[j+1]),j比k好当且仅当(f[j]-f[k])/(宽[k+1]-宽[j+1])<长[i]。
2、bzoj1010 http://www.lydsy.com/JudgeOnline/problem.php?id=1010
题意:n个东西,每个有一个长度Ci。要将这些东西分成几段,每段中东西编号连续。东西编号从i到j的段长度为x=i-j+sigma(k,i,j)Ck,费用为(x-L)^2(L为常量),求最小费用。n≤50000
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 50500 6 #define ll long long 7 using namespace std; 8 9 inline int read(){ 10 char ch=getchar(); int f=1,x=0; 11 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} 12 while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 13 return f*x; 14 } 15 int n,l,r,q[maxn]; ll L,sum[maxn],f[maxn]; 16 ll sqr(ll x){return x*x;} 17 double calc(int j,int k){ 18 return (double)(f[j]-f[k]+sqr(j+sum[j])-sqr(k+sum[k]))/(double)(j+sum[j]-k-sum[k]); 19 } 20 int main(){ 21 n=read(); L=(ll)read(); inc(i,1,n){ll a=(ll)read(); sum[i]=sum[i-1]+a;} l=r=1; 22 inc(i,1,n){ 23 while(l<r&&calc(q[l],q[l+1])<2*(i+sum[i]-L-1))l++; 24 f[i]=f[q[l]]+sqr((ll)(i-q[l]-1)+sum[i]-sum[q[l]]-L); 25 while(l<r&&calc(q[r-1],q[r])>calc(q[r],i))r--; q[++r]=i; 26 } 27 printf("%lld",f[n]); return 0; 28 }
题解:裸斜率优化dp:f[i]=f[j]+((i-j-1)+sum[i]-sum[j]-L)^2,j比k好当且仅当(f[j]-f[k]+(j+sum[j])^2-(k+sum[k])^2)/(j+sum[j]-k-sum[k])>2*(i+sum[i]-L-1)。注意longlong。
3、bzoj3211 http://www.lydsy.com/JudgeOnline/problem.php?id=3211
题意:n个数的序列,m个操作,操作两种:区间开根(向下取整)和区间求和。n≤100000,m≤200000,序列中的数非负且≤109。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 100500 7 #define ll long long 8 #define lb(x) x&-x 9 using namespace std; 10 11 inline int read(){ 12 char ch=getchar(); int f=1,x=0; 13 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1,ch=getchar();} 14 while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 15 return f*x; 16 } 17 int n,m,fa[maxn]; ll c[maxn],v[maxn]; 18 void update(int x,ll val){while(x<=n)c[x]+=val,x+=lb(x);} 19 ll query(int x){ll q=0; while(x>0)q+=c[x],x-=lb(x); return q;} 20 int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} 21 int main(){ 22 n=read(); inc(i,1,n)v[i]=(ll)read(),fa[i]=i,update(i,v[i]); m=read(); fa[n+1]=n+1; 23 inc(i,1,m){ 24 int x=read(),l=read(),r=read(); 25 if(x==1)printf("%lld\n",query(r)-query(l-1)); 26 if(x==2){ 27 int j=l; 28 while(j<=r){ 29 j=find(j); if(j>r)break; ll y=v[j]; v[j]=(ll)sqrt(y); 30 update(j,v[j]-y); if(v[j]==1||v[j]==0)fa[j]=find(j+1); j++; 31 } 32 } 33 } 34 return 0; 35 }
题解:一个≤109的数开6次根就变成1了。因此开根操作可以暴力只开不是1或0的数。对每个数维护并查集表示离它最近的不是1或0的数,每次只修改这个数在并查集中的根节点,然后跳到根节点的下一个数继续此操作。而数组的快速修改求和用树状数组就可以了。反思:本机测大数据会爆栈,路径压缩得写出非递归形式,但似乎bzoj的栈很大。
4、bzoj2753 http://www.lydsy.com/JudgeOnline/problem.php?id=2753
题意:n点m有权边图,每个点都有高度,只能从高度高的点到高度低的点。同时还可以瞬移到走过的点,希望求经过最多点的最短时间。n≤100000,m≤1000000。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 100500 7 #define ll long long 8 using namespace std; 9 10 struct e{int f,t; ll w; int n;}; e es[maxn*20]; int g[maxn],ess,h[maxn]; 11 void pe(int f,int t,ll w){es[++ess]=(e){f,t,w,g[f]}; g[f]=ess;} 12 bool cmp(e a,e b){return h[a.t]!=h[b.t]?h[a.t]>h[b.t]:a.w<b.w;} 13 inline int read(){ 14 char ch=getchar(); int f=1,x=0; 15 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} 16 while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 17 return f*x; 18 } 19 bool vis[maxn]; queue <int> q; int n,m,ans1,fa[maxn],tot; ll ans2; 20 void bfs(int s){ 21 while(!q.empty())q.pop(); memset(vis,0,sizeof(vis)); q.push(s); vis[s]=1; ans1=1; 22 while(! q.empty()){ 23 int x=q.front(); q.pop(); 24 for(int i=g[x];i;i=es[i].n)if(!vis[es[i].t])vis[es[i].t]=1,q.push(es[i].t),ans1++; 25 } 26 } 27 int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} 28 int main(){ 29 n=read(); m=read(); inc(i,1,n)h[i]=read(); 30 inc(i,1,m){ 31 int a=read(),b=read(); ll c=(ll)read(); 32 if(h[a]>=h[b])pe(a,b,c); if(h[a]<=h[b])pe(b,a,c); 33 } 34 bfs(1); sort(es+1,es+ess+1,cmp); inc(i,1,n)fa[i]=i; ans2=tot=0; 35 inc(i,1,ess){ 36 int x=find(es[i].f),y=find(es[i].t); if(!vis[es[i].f]||!vis[es[i].t]||x==y)continue; 37 fa[x]=y; tot++; ans2+=es[i].w; if(tot==ans1-1)break; 38 } 39 printf("%d %lld\n",ans1,ans2); return 0; 40 }
题解:
“
第一问:用bfs扩展出能到达的所有点,并标记。第二问:分层做最小生成树。最后一个问题:怎么分层呢?其实很简单,最小生成树之前要把边排序,这个时候我们把高度作为第一关键字,然后高度相同再按照边权排序,这样就分层了啊
”
感觉很神的样子。反思:因为边数弄错wa了好几发QAQ~
5、bzoj2427 http://www.lydsy.com/JudgeOnline/problem.php?id=2427
题意:有n个软件,每个大小为wi,价值为vi,同时每个软件依赖0个或一个其他软件,要求在大小不超过的m的前提下得到最大价值。n≤100,m≤500。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define dec(i,j,k) for(int i=j;i>=k;i--) 6 #define maxn 150 7 using namespace std; 8 9 int bel[maxn],scc,dfn[maxn],low[maxn],tim,st[maxn],top,sv[maxn],sw[maxn],v[maxn],w[maxn]; bool inst[maxn],ok[maxn]; 10 struct e1{int f,t,n;}; e1 es1[maxn*5]; int g1[maxn],ess1; 11 void pe1(int f,int t){es1[++ess1]=(e1){f,t,g1[f]}; g1[f]=ess1;} 12 struct e2{int t,n;}; e2 es2[maxn*10]; int g2[maxn],ess2; 13 void pe2(int f,int t){es2[++ess2]=(e2){t,g2[f]}; g2[f]=ess2; ok[t]=1;} 14 void init(){ 15 ess1=ess2=0; memset(g1,0,sizeof(g1)); memset(g2,0,sizeof(g2)); 16 tim=scc=0; memset(inst,0,sizeof(inst)); memset(dfn,0,sizeof(dfn)); 17 } 18 void tarjan(int x){ 19 dfn[x]=low[x]=++tim; st[++top]=x; inst[x]=1; 20 for(int i=g1[x];i;i=es1[i].n){ 21 if(!dfn[es1[i].t]){ 22 tarjan(es1[i].t); low[x]=min(low[x],low[es1[i].t]); 23 }else if(inst[es1[i].t])low[x]=min(low[x],dfn[es1[i].t]); 24 } 25 if(dfn[x]==low[x]){ 26 scc++; int y=0; while(x!=y)y=st[top],bel[y]=scc,sv[scc]+=v[y],sw[scc]+=w[y],inst[y]=0,top--; 27 } 28 } 29 void build(){inc(i,1,ess1)if(bel[es1[i].f]!=bel[es1[i].t])pe2(bel[es1[i].f],bel[es1[i].t]);} 30 int f[maxn][maxn*5],n,m; 31 void dfs(int x){ 32 for(int i=g2[x];i;i=es2[i].n){ 33 dfs(es2[i].t); 34 dec(j,m-sw[x],0)inc(k,0,j)f[x][j]=max(f[x][j],f[x][k]+f[es2[i].t][j-k]); 35 } 36 dec(j,m,0)if(j>=sw[x])f[x][j]=f[x][j-sw[x]]+sv[x];else f[x][j]=0; 37 } 38 inline int read(){ 39 char ch=getchar(); int f=1,x=0; 40 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} 41 while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 42 return f*x; 43 } 44 int main(){ 45 n=read(); m=read(); inc(i,1,n)w[i]=read(); inc(i,1,n)v[i]=read(); init(); 46 inc(i,1,n){int a=read(); if(a)pe1(a,i);} inc(i,1,n)if(!dfn[i])tarjan(i); build(); 47 inc(i,1,scc)if(!ok[i])pe2(scc+1,i); dfs(scc+1); printf("%d",f[scc+1][m]); return 0; 48 }
题解:缩点然后做“树上背包dp”,具体看代码,注意里面用到了滚动数组。
6、bzoj1041 http://www.lydsy.com/JudgeOnline/problem.php?id=1041
题意:求一个给定半径的圆圆周上有多少个点的坐标是整数。r≤2*109
代码:
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <cmath> 5 #define ll long long 6 #define inc(i,j,k) for(ll i=j;i<=k;i++) 7 using namespace std; 8 9 ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);} 10 int main(){ 11 ll n,ans=0; scanf("%lld",&n); n*=2; 12 inc(i,1,(ll)sqrt(n))if(n%i==0){ 13 ll d=i; 14 inc(j,1,(ll)sqrt(n/d/2)){ 15 ll b=n/d-j*j; if(sqrt(b)==(double)((ll)sqrt(b))&&gcd(j*j,b)==1&&j*j!=b)ans++; 16 } 17 if(d!=n/i){ 18 d=n/i; 19 inc(j,1,(ll)sqrt(n/d/2)){ 20 ll b=n/d-j*j; if(sqrt(b)==(double)((ll)sqrt(b))&&gcd(j*j,b)==1&&j*j!=b)ans++; 21 } 22 } 23 } 24 printf("%lld",ans*4+4); return 0; 25 }
题解:数学神题,本弱只能转载一下黄学长的题解
“
首先x²+y²=r²,变形得y²=(r+x)*(r-x)。令d=gcd(r+x,r-x),则A=(r-x)/d,B=(r+x)/d,A,B互质,用a,b代入,有y²=d²*A*B,由于d²,y²为完全平方数,故A*B也为完全平方数。又因为A≠B,所以A和B都是完全平方数。设a²=A=(r-x)/d,b²=B=(r+x)/d,则a²+b²=2r/d,因此d为r的约数。
有了上面的推理,那么实现的方法为:枚举d∈[1,sqrt(2R)],然后根据上述推理可知:必先判d是否为2R的一约数。此时d为2R的约数有两种情况:d=d或d=2R/d。
第一种情况:d=2R/d。枚举a∈[1,sqrt(2R/d/2)] ,算出对应的b=sqrt(2R/d-a^2),检查是否此时的A,B满足:A≠B且A,B互质,若是就将答案加1
第二种情况:d=d。枚举a∈[1,sqrt(d/2)],算出对应的b=sqrt(d-a^2),检查是否此时的A,B满足:A≠B且A,B互质 <根据上面的推理可知必需满足此条件>,若是就将答案加1
因为这样只算出了第一象限的情况<上面枚举时均是从1开始枚举>,根据圆的对称性,其他象限的整点数与第一象限中的整点数相同,最后,在象限轴上的4个整点未算,加上即可,那么最后答案为ans=4*第一象限整点数+4
”
7、bzoj1013 http://www.lydsy.com/JudgeOnline/problem.php?id=1013
题意:给定n维球体上n+1个点的坐标,求球心坐标。n≤10
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 #define maxn 20 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 #define eps 1e-6 8 using namespace std; 9 10 double a[maxn][maxn],f[maxn]; int n; 11 double sqr(double x){return x*x;} 12 bool gauss(){ 13 int now=1,pos; double t; 14 inc(i,1,n){ 15 for(pos=now;pos<=n;pos++)if(fabs(a[pos][i])>eps)break; if(pos>n)continue; 16 if(pos!=now){inc(j,1,n+1)swap(a[pos][j],a[now][j]);} t=a[now][i]; inc(j,1,n+1)a[now][j]/=t; 17 inc(j,1,n)if(j!=now){t=a[j][i]; inc(k,1,n+1)a[j][k]-=t*a[now][k];} 18 now++; 19 } 20 inc(i,now,n)if(fabs(a[i][n+1])>eps)return 0; return 1; 21 } 22 int main(){ 23 scanf("%d",&n); inc(i,1,n)scanf("%lf",&f[i]); 24 inc(i,1,n)inc(j,1,n){double b; scanf("%lf",&b); a[i][j]=2*b-2*f[j]; a[i][n+1]+=sqr(b)-sqr(f[j]);} 25 gauss(); 26 inc(i,1,n-1)printf("%.3lf ",a[i][n+1]); printf("%.3lf\n",a[n][n+1]); 27 }
题解:考虑二维情况,设球心坐标为x,y,第一个坐标为x‘,y‘,则可得方程(x-x‘)²+(y-y‘)²=r²,然后从第二个坐标开始都可以和第一个坐标联立并化简,有了n个方程就可以高斯消元解出球心坐标了,多维情况也很容易推广。反思:第一次写高斯消元。高斯消元的主要思想是将系数矩阵化成倒三角矩阵(满足matrix[i][j],i>j都为0的矩阵),对于这个矩阵,如果斜线上(即matrix[i][i])有系数为0且结果矩阵也为0,那么这个元为自由元(可取任何数);如果斜线上有系数为0且结果矩阵不为0,那么该方程无解;否则该方程有唯一解。高斯消元本来的解法是求出倒三角矩阵后用最后一个方程回代,然而如果一开始就知道解的情况,就可以免回代,在求倒三角矩阵的同时顺便消元。
8、bzoj1923 http://www.lydsy.com/JudgeOnline/problem.php?id=1923
题意:n只两种动物,一种有奇数只脚,另一种偶数只角。现在进行m次操作,每次告诉你若干只动物的脚数之和为奇数还是偶数。要求你输出所有动物的类型以及最少多少次操作就能判断。n≤1000,m≤10000
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <bitset> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 1010 7 using namespace std; 8 9 bitset <maxn> M[maxn*2]; int n,m,ans; char s[maxn]; 10 void gause(){ 11 int now=0,pos; 12 inc(i,1,n){ 13 for(pos=now+1;pos<=m&&!M[pos][i];pos++); if(pos==m+1){ans=-1; return;}else ans=max(ans,pos); 14 now++; swap(M[now],M[pos]); inc(j,1,m)if(j!=now&&M[j][i])M[j]^=M[now]; 15 } 16 } 17 int main(){ 18 scanf("%d%d",&n,&m); 19 inc(i,1,m){scanf("%s",s+1); inc(j,1,n)M[i][j]=s[j]-‘0‘; int a; scanf("%d",&a); M[i][n+1]=a;} 20 gause(); 21 if(ans==-1)printf("Cannot Determine");else{ 22 printf("%d\n",ans); inc(i,1,n)printf(M[i][n+1]?"?y7M#\n":"Earth\n"); 23 } 24 return 0; 25 }
题解:设放进去的动物的系数为1,没放的系数为0,脚数如果是奇数结果就为1,偶数结果为0,解异或方程,具体看代码。
9、bzoj3503 http://www.lydsy.com/JudgeOnline/problem.php?id=3503
题意:我们称一个由0和1组成的矩阵是和谐的,当且仅当每个元素都有偶数个相邻的1。一个元素相邻的元素包括它本身,及他上下左右的4个元素(如果存在)。给定矩阵的行数和列数,计算并输出一个和谐的矩阵。注意:所有元素为0的矩阵是不允许的。行列数≤40
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <bitset> 5 #define maxn 50 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 #define ll long long 8 using namespace std; 9 10 bitset <maxn> M[maxn]; 11 ll a[maxn][maxn];int b[maxn][maxn],n,m; 12 void gauss(){ 13 int now=0,pos; 14 inc(i,1,m){ 15 for(pos=now+1;pos<=m&&!M[pos][i];pos++); if(pos>m)continue; 16 now++; swap(M[pos],M[now]); inc(j,now+1,m)if(M[j][i])M[j]^=M[now]; 17 } 18 for(int i=m;i>=1;i--){ 19 b[1][i]=M[i][m+1]; if(!M[i][i]){b[1][i]=1; continue;} inc(j,i+1,m)if(M[i][j])b[1][i]^=b[1][j]; 20 } 21 } 22 int main(){ 23 scanf("%d%d",&n,&m); inc(i,1,m)a[1][i]=(ll)1<<(i-1); 24 inc(i,2,n+1)inc(j,1,m)a[i][j]=a[i-2][j]^a[i-1][j-1]^a[i-1][j+1]^a[i-1][j]; 25 inc(i,1,m){inc(j,1,m)M[i][j]=((ll)1<<(j-1)&a[n+1][i])?1:0; M[i][m+1]=0;} gauss(); 26 inc(i,2,n)inc(j,1,m)b[i][j]=b[i-2][j]^b[i-1][j-1]^b[i-1][j+1]^b[i-1][j]; 27 inc(i,1,n){inc(j,1,m-1)printf("%d ",b[i][j]); printf("%d\n",b[i][m]);} 28 return 0; 29 }
题解:设矩阵为a,则a[i][j]^a[i+1][j]^a[i-1][j]^a[i][j-1]^a[i][j+1]为0,用i-1替换i则得a[i-1][j]^a[i][j]^a[i-2][j]^a[i-1][j-1]^a[i-1][j+1]=0,每一个元素都与其上面的元素相关,因此可以说第一行的元素决定了所有元素。同时第一行的填法合法当且仅当用第一行推出a[m+1][i]的所有元素都为0。故可以找到a[m+1][j]与第一行哪些元素相关,然后列异或方程组。这个过程可以用二进制弄。如何保证不出现所有元素为0的矩阵出现呢?只要高斯消元时把自由元都当做1就行了,这样一来就必须回代了。反思:二进制操作要用到longlong,然而我强制转换乱写一通导致我wa了n次。尤其是这个地方:M[i][j]=((ll)1<<(j-1)&a[n+1][i])?1:0,我原来是这样写的M[i][j]=((ll)(1<<(j-1)&a[n+1][i])?1:0,这两种解法不同是因为后者先将乘法算出来并溢出了,然后才被转换,而前者不同是因为它把乘数转换了,而longlong*int的结果为longlong故不会溢出,以后要记牢这一点。
10、bzoj1968 http://www.lydsy.com/JudgeOnline/problem.php?id=1968
题意:定义f(x)=x的约数个数,求sigma(i,1,n)f(i)。n≤1000000
代码:
1 #include <cstdio> 2 using namespace std; 3 int main(){ 4 int n;scanf("%d",&n); int ans=0; 5 for(int i=1;i<=n;i++)ans+=n/i; 6 printf("%d",ans); return 0; 7 }
题解:只要会思路这道题就很水。对于一个数i,它是n/i个数的约数,对答案有n/i的贡献。所以直接从1枚举到n累加n/i就行了。
11、bzoj1212 http://www.lydsy.com/JudgeOnline/problem.php?id=1212
题意:给定一个字典D,你的程序需要判断若干段文章在字典D下是否能够被理解。 并给出其在字典D下能够被理解的最长前缀的位置。理解定义为这段文章可以拆成字典里的单词。单词数≤10且长度≤10,文章数≤20且长度≤1M。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 200 6 using namespace std; 7 8 int ch[maxn][26],n,m,sz; bool d[maxn*10000],val[maxn]; char s[maxn*10000]; 9 void insert(char *s){ 10 int l=strlen(s+1),x=0; 11 inc(i,1,l){ 12 if(!ch[x][s[i]-‘a‘])ch[x][s[i]-‘a‘]=++sz; x=ch[x][s[i]-‘a‘]; 13 } 14 val[x]=1; 15 } 16 int main(){ 17 scanf("%d%d",&n,&m); inc(i,1,n)scanf("%s",s+1),insert(s); 18 inc(i,1,m){ 19 memset(d,0,sizeof(d)); scanf("%s",s+1); int l=strlen(s+1); d[0]=1; 20 inc(i,1,l)if(d[i-1]){ 21 int x=0,y=i; 22 while(s[y]-‘a‘>=0&&ch[x][s[y]-‘a‘]){ 23 x=ch[x][s[y]-‘a‘]; if(val[x])d[y]=1; y++; 24 } 25 } 26 while(!d[l])l--; printf("%d\n",l); 27 } 28 return 0; 29 }
题解:在trie上跑dp,dp[i]表示文章能否匹配到i这个位置。对于每个i,如果dp[i-1]为1,则从s[i]开始在trie上走,走过的节点数+i的dp值都置为1。