NOIP 2015 提高组 day2 解题报告

来源:互联网 发布:ajaxupload.js cdn 编辑:程序博客网 时间:2024/05/18 03:08

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+50using 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]=s[i1][j][k]+f[i][j][k]

可以这样思考

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次,然后现在再匹配一次

所以可以得到

f[i][j][k]=f[i1][j1][k]+s[i1][j1][k1]

如果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 1000000007using 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 300010using 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;}

这里写图片描述

这里写图片描述

原创粉丝点击