考察知识:二分,模拟
算法难度:XX+ 实现难度:XX+
分析:因为答案具有单调性(或者说这是最大最小问题,为T3做铺垫),我们考虑二分解决
我们二分出最短跳跃距离的最大值mid,然后进行判断:判断至少要移除多少块石头才能满足条件
至于怎么判断,我们可以写一个判断函数,用模拟的方法统计
#include
int L,n,m,a[50005];
bool check(int limt){
int cnt=0,l=0;//l表示当前石头左边石头(考虑有些石头已经被移除)的坐标
for(int i=1;i<=n+1;i++)
if(a[i]-l>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}
考察知识:动态规划,字符串
算法难度:XXXX 实现难度:XX+
我的动态规划水平真的非常差,所以并没有独立想出来,参考了题解:P2679题解
下面为了加深印象,自己写一遍分析:
分析:
设f[i][j][k]为A用到了i,B用到了j,已经用了k个子串,
并且一定用了当前字符(A[i])时的方案数。
设g[i][j][k]为A用到了i,B用到了j,已经用了k个子串,
无论用不用当前字符(A[i])时的方案数总和。
1.A匹配的子串可以是不连续的,而B必须全部匹配
2.选子串时可以A[ i ]可以选,可以不选,而B[ i ]必须选
定义:
表示字符串A匹配了1...i,字符串B匹配了1...j,且B已经匹配了k个子串,且A[ i ]被选中为子串的方案数
表示字符串A匹配了1...i,字符串B匹配了1...j,且B已经匹配了k个子串,且A[ i ]可能被选中为子串的方案数
说明:
1.由的定义,当
选中A[ i ]时有
种情况
当不选中A[ i ]时,有种情况,因为:不选A[ i ] 那么可能选A[ i-1 ]所以为
2.首先必须有A[ i ]==B[ j ]:当A[ i ] 与A[ i-1 ]共同包含在第k个子串时,有:种情况
其次,当A[ i ]与A[ x ](0
然后我们不难得出完整的状态转移方程:
对于:
当A[ i ]==B[ j ]时:
当A[ i ]!=B[ j ]时:
对于:
边界:
代码:(用滚动数组优化)
#include
#include
#include
using namespace std;
const int MOD=1000000007;
char A[1005],B[205];
int n,m,k,f[2][205][205],g[2][205][205];
/*
if(A[i]==B[j])
f[i][j][k]=f[i-1][j-1][k-1]+g[i-1][j-1][k]
else
f[i][j][k]=0;
g[i][j][k]=g[i-1][j][k]+f[i][j][k]
*/
int main(){
scanf("%d%d%d",&n,&m,&k);
scanf("%s%s",A+1,B+1);
g[0][0][0]=1;
for(int i=1;i<=n;i++){
g[i%2][0][0]=1;
for(int j=1;j<=m;j++)
for(int K=1;K<=k;K++){
if(A[i]==B[j])
f[i%2][j][K]=(f[(i-1)%2][j-1][K]+g[(i-1)%2][j-1][K-1])%MOD;
else
f[i%2][j][K]=0;
g[i%2][j][K]=(g[(i-1)%2][j][K]+f[i%2][j][K])%MOD;
}
}
printf("%d\n",g[n%2][m][k]);
return 0;
}
考察知识:LCA,二分,树上差分,树链剖分
算法难度:XXXX+ 实现难度:XXXX
说明:在看下面题解之前,请确保你已经掌握了“考察知识”中除了“树链剖分”的所有内容
分析:确实有难度,但是如果你想出了算法,就会发现这道题其实就是一堆模板(LCA,树上差分,二分)的合集。
算法流程+分析:
1.我们首先计算所有运输计划每一个需要的时间,然后按时间从小到大排序。我们可以用LCA或树链剖分解决。
2.接着我们要考虑删边了,我们应该删除那一条边呢?枚举显然TLE。因为是问删边后的最大时间时间最小,是最大最小问题,我们考虑用二分解决。
3.我们二分求删边后最小的最大时间,设为mid,那么我们找出所有时间大于mid的运输计划,假设有k个,然后我们树上差分,将所有时间大于mid的运输计划需要经过的边标记+1。
4.我们dfs计算差分数组,然后枚举标记值等于k的所有的边,如果有一条边: 经过这条边的需要时间+mid>=不删边的最大时间 ,那么mid满足条件,否则不满足,然后我们继续二分就可以了。
代码:
说明:我采用的树链剖分计算LCA和经过边的边权,建议大家自己写代码,这道题细节还是比较多,代码量也比较大,比较容易写错。如果自行用多种方法写出代码,对能力提升是较大的。
#include
#include
#include
#include
using namespace std;
const int maxn=300005;
char ch;
void scan(int& in_){
ch=getchar();
while(!isdigit(ch)) ch=getchar();
in_=0;
while(isdigit(ch))in_=in_*10+ch-'0',ch=getchar();
}
struct transport_plan{
int u,v,L,lca;
friend bool operator < (const transport_plan& A,const transport_plan& B){
return A.Ldep[y]) swap(x,y);
ret+=S[seg[y]]-S[seg[x]];
return ret;
}
int ask_lca(int x,int y){
int fx=top[x],fy=top[y];
while(fx!=fy){
if(dep[fx]dep[y]) swap(x,y);
return x;
}
//-------------^END^-----------------
int n,m,l,r,k,C[maxn],CC[maxn];
void dfs3(int i,int Fa){//树上差分计算
CC[i]=C[i];
for(int p=head[i];p;p=e[p].next){
int j=e[p].to;
if(j==Fa) continue;
dfs3(j,i);
CC[i]+=CC[j];
}
}
bool check(int limt){//二分判断函数
tmp.L=limt;
int i=upper_bound(TP+1,TP+m+1,tmp)-TP;
k=m-i+1;
memset(C,0,sizeof(C));
for(;i<=m;i++)
C[TP[i].u]++,C[TP[i].v]++,C[TP[i].lca]-=2;
dfs3(1,0);
for(int ii=1;ii<=n;ii++)
if(CC[ii]>=k&&TP[m].L-D[ii]<=limt) return true;
return false;
}
void build(){
int u,v,L;
scan(n),scan(m);
for(int i=1;i>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
}
int main(){
build();
solve();
return 0;
}