T1 跳石头:
题目描述 Description
一年一度的“跳石头”比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有N块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M块岩石(不能移走起点和终点的岩石)。
输入描述 Input Description
输入文件名为 stone.in。
输入文件第一行包含三个整数L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。
接下来N行,每行一个整数,第i行的整数Di(0 < Di < L)表示第i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
输出描述 Output Description
输出文件名为stone.out。
输出文件只包含一个整数,即最短跳跃距离的最大值。
样例输入 Sample Input
25 5 2
2
11
14
17
21
样例输出 Sample Output
4
数据范围及提示 Data Size & Hint
对于20%的数据,0≤M≤N≤10。 对于50%的数据,0≤M≤N≤100。
对于50%的数据,0≤M≤N≤100。
对于100%的数据,0≤M≤N≤50,000,1≤L≤1,000,000,000。
题解:
二分答案裸题,对于此类求最小值最大,很多时候需要二分一个答案,这个时候我们进行二分,然后在check(也就是我的query函数)的时候进行计数,如果计的数大于了需要移走的个数,则说明我的距离大了,导致必须移动更多的石头来满足二分到的答案,因此把距离往小二分,反之把距离往大二分
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#define MAXN 50000+50
using namespace std;
int L,N,M;
int cnt;
int loc[MAXN];
int maxn;
int len[MAXN];
int query(int num){
int last=0;
int cnt=0;
for(register int i=1;i<=N+1;i++){
if(loc[i]-last<num) cnt++;
else last=loc[i];
}
return cnt;
}
int main(){
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
scanf("%d%d%d",&L,&N,&M);
for(register int i=1;i<=N;i++){
scanf("%d",&loc[i]);
len[i]=loc[i]-loc[i-1];
maxn=max(maxn,len[i]);
}
loc[N+1]=L;
len[N+1]=loc[N+1]-loc[N];
maxn=max(maxn,len[N+1]);
int l=0;
int r=maxn;
while(l<=r){
int mid=(l+r)>>1;
int temp=query(mid);
if(temp>M) r=mid-1;
else l=mid+1;
}
//int te=lower_bound(len+1,len+N+1,l)-len;
printf("%d",l-1);
return 0;
}
这里面的len数组没有什么卵用…懒得删了,之前的思路不一样
对于printf的l-1可以这样思考:
对于while,如果当前的l已经等于r了,那么如果query(mid)成立,那么l就会++,然而大于mid的都是不成立的,所以这个时候输出l–也就是之前的mid,如果query(mid)不成立,那么r就会- -,并且l这处也是不成立的,所以往左走,因为左边的数据都是满足的(因为每次都是只要满足就往右走)
T2:子串
时间限制: 1 s
空间限制: 128000 KB
题目描述 Description
有两个仅包含小写英文字母的字符串A和B。现在要从字符串A中取出k个互不重叠的非空子串,然后把这k个子串按照其在字符串A中出现的顺序依次连接起来得到一个新的字符串,请问有多少种方案可以使得这个新串与字符串B相等?注意:子串取出的位置不同也认为是不同的方案。
输入描述 Input Description
第一行是三个正整数n,m,k,分别表示字符串A的长度,字符串B的长度,以及问题描述中所提到的k,每两个整数之间用一个空格隔开。
第二行包含一个长度为n的字符串,表示字符串A。 第三行包含一个长度为m的字符串,表示字符串B。
输出描述 Output Description
输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求输出答案对1,000,000,007取模的结果。
样例输入 Sample Input
【Input1】
6 3 1
aabaab
aab
【Input2】
6 3 2
aabaab
aab
【Input3】
6 3 3
aabaab
aab
样例输出 Sample Output
【Output1】
2
【Output2】
7
【Output3】
7
数据范围及提示 Data Size & Hint
对于第1组数据:1≤n≤500,1≤m≤50,k=1;
对于第2组至第3组数据:1≤n≤500,1≤m≤50,k=2;
对于第4组至第5组数据:1≤n≤500,1≤m≤50,k=m;
对于第1组至第7组数据:1≤n≤500,1≤m≤50,1≤k≤m;
对于第1组至第9组数据:1≤n≤1000,1≤m≤100,1≤k≤m;
对于所有10组数据:1≤n≤1000,1≤m≤200,1≤k≤m。
题解:
显然是一道dp题
设两串字符的数组名分别为A和B
我们令
f[i][j][k]
表示当前匹配到A的i-1个数,B的j-1个数,当前匹配k次(即提取了k个A的子串),且A中的第i为恰好被用到
令
s[i][j][k]
表示当前匹配到A的i-1个数,B的j-1个数,当前匹配了k次(即提取了k个A的子串),且A中的第i个不一定恰好被用到
我们可以自然地推出s的状态转移
可以这样思考
s[i][j][k] 等于不选A的第i个字符和选A的第i个字符的情况和
那么 f[i][j][k] 的值怎么算呢?
在匹配
f[i][j][k]
的时候我们其实是在匹配
A[i]
和
B[j]
如果
A[i]==B[i]
,那么我们有两种选择
第一种是让目前匹配上的和之前的合并,也是匹配了k次,方案数不变
还有一种是让目前的匹配自成一派,也就是之前匹配了k-1次,然后现在再匹配一次
所以可以得到
如果 A[i]≠B[i]
那么显然此时的 f[i][j][k]=0 原因是f数组表示的是刚好要取i-1时才有值,否则值为0
所以现在我们已经得到了所有的s和f数组的状态转移,显然最后的答案在 s[n][m][k] 中
显然如果两个数组都开三维的话,是会爆空间的,所以我们需要用滚动来优化空间,就像背包一样~
#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 1000+10
#define MINN 200+20
#define idy 1000000007
using namespace std;
int N,M,K;
char A[MAXN],B[MAXN];
int f[MAXN][MINN],s[MAXN][MINN];
int main(){
scanf("%d%d%d%s%s",&N,&M,&K,&A,&B);
s[0][0]=1;
for(register int i=1;i<=N;i++){
for(register int j=M;j>=1;j--){
if(A[i-1]==B[j-1])
for(register int k=min(j,K);k>=1;k--){
f[j][k]=(f[j-1][k]+s[j-1][k-1])%idy;
s[j][k]=(s[j][k]+f[j][k])%idy;
}else fill(f[j],f[j]+min(j,K)+1,0);
}
}
printf("%d",s[M][K]%idy);
return 0;
}
T3:运输计划
题目描述 Description
公元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?
输入描述 Input Description
第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1<=ai,bi<=n 且 0<=ti<=1000。接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1<=ui,vi<=n
输出描述 Output Description
输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。
样例输入 Sample Input
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
样例输出 Sample Output
11
数据范围及提示 Data Size & Hint
样例解释:
将第 1 条航道改造成虫洞: 则三个计划耗时分别为:11,12,11,故需要花费的时间为 12。
将第 2 条航道改造成虫洞: 则三个计划耗时分别为:7,15,11,故需要花费的时间为 15。
将第 3 条航道改造成虫洞: 则三个计划耗时分别为:4,8,11,故需要花费的时间为 11。
将第 4 条航道改造成虫洞: 则三个计划耗时分别为:11,15,5,故需要花费的时间为 15。
将第 5 条航道改造成虫洞: 则三个计划耗时分别为:11,10,6,故需要花费的时间为 11。
故将第 3 条或第 5 条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为 11。
测试数据及约定:
所有数据:
1 <= ai, bi, uj, vj <= n, 0 <= ti <= 1000
这道题显然也是一个二分加LCA加差分裸题,比较水,只需要将所有询问的LCA处理出来,然后每次二分最长路径长度,然后寻找非法路径,然后寻找所有路径的交,再在所有路径的交里寻找一条最长的边,然后对所有非法路径减去这条边,判断一下是否满足二分的值就好了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#define MAXN 300010
using namespace std;
int readin()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Line{
int from,to,val,nxt;
}line[MAXN*2];
struct queryL{
int from,to,nxt,lca;
}question[MAXN*2];
int n,m,s[MAXN],f[MAXN],dfn[MAXN],dis[MAXN],fa[MAXN],head_line[MAXN],head_question[MAXN],tail_line,tail_question,tail_dfs;
void add_line(int from,int to,int val){
tail_line++;
line[tail_line].from=from;
line[tail_line].to=to;
line[tail_line].val=val;
line[tail_line].nxt=head_line[from];
head_line[from]=tail_line;
}
void add_question(int from,int to){
tail_question++;
question[tail_question].from=from;
question[tail_question].to=to;
question[tail_question].nxt=head_question[from];
head_question[from]=tail_question;
}
void dfs(int u,int father,int cnt){
fa[u]=father;
dis[u]=cnt;
for(register int i=head_line[u];i;i=line[i].nxt){
int v=line[i].to;
if(v!=father) dfs(v,u,cnt+line[i].val);
}
dfn[++tail_dfs]=u;
}
int find(int x){if(x==f[x]) return x;else return f[x]=find(f[x]);}
void tarjan(int u){
f[u]=u;
for(register int i=head_line[u];i;i=line[i].nxt){
int v=line[i].to;
if(v!=fa[u]){
tarjan(v);
f[v]=u;
}
}
for(register int i=head_question[u];i;i=question[i].nxt){
int t=question[i].to;
if(f[t]) question[i].lca=find(t);
}
}
bool check(int maxn){
int ans=0,total=0,Maxn=0;
memset(s,0,sizeof(s));
for(register int i=1;i<=tail_question;i+=2){
question[i].lca=max(question[i].lca,question[i+1].lca);
int len=dis[question[i].from]+dis[question[i].to]-2*dis[question[i].lca];
if(len>maxn){
total++;
s[question[i].from]++;
s[question[i].to]++;
s[question[i].lca]-=2;
Maxn=max(Maxn,len-maxn);
}
}
for(register int i=1;i<=n;i++) s[fa[dfn[i]]]+=s[dfn[i]];
for(register int i=1;i<=n;i++){
if(s[i]==total) ans=max(ans,dis[i]-dis[fa[i]]);
}
return ans>=Maxn;
}
int main(){
freopen("transport.in","r",stdin);
freopen("transport.out","w",stdout);
n=readin();m=readin();
int from,to,val,l=0,r=0;
for(register int i=1;i<=n-1;i++){
from=readin();to=readin();val=readin();
add_line(from,to,val),add_line(to,from,val);
r+=val;
}
for(register int i=1;i<=m;i++){
from=readin();to=readin();
add_question(from,to);
add_question(to,from);
}
dfs(1,0,0);
tarjan(1);
int ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
return 0;
}