NOIP2015 day2 解题报告

做题注意要把自己检验程序时多余printf的东西删掉啊,今天两道题ole…
第一题:
1 . 跳石头
(stone.cpp/c/pas)
【问题描述】
一年一度的“跳石头”比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选
择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终
点的岩石) 。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达
终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳
跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能
移走起点和终点的岩石) 。
【输入格式】
输入文件名为 stone.in。
输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终
点之间的岩石数,以及组委会至多移走的岩石数。
接下来 N 行,每行一个整数,第 i 行的整数 Di( 0 < Di < L )表示第 i 块岩石与
起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同
一个位置。
【输出格式】
输出文件名为 stone.out。
输出文件只包含一个整数,即最短跳跃距离的最大值。
【输入输出样例 1】
stone.in stone.out
25 5 2
2
11
14
17
21
4
见选手目录下的 stone/stone1.in 和 stone/stone1.ans。
【输入输出样例 1 说明】
将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离
17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点) 。
【输入输出样例 2】
见选手目录下的 stone/stone2.in 和 stone/stone2.ans。
【 数据规模与约定 】
对于 20%的数据,0 ≤ M ≤ N ≤ 10。
对于 50%的数据,0 ≤ M ≤ N ≤ 100。
对于 100%的数据,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

题解:
这道题显然的二分答案,我们二分一下最小跳跃距离,每次check o(n)扫一遍看一下相邻两个石头距离有没有大于当前mid的,有的话就cnt++,最后cnt(即要移走的石头数)有没有大于题目中的K,没有的话则mid正确返回true.

#include<stdio.h>
int last,cnt,a[50005],n,m,L,lf,rg,ans,mid;
bool check(int len){
    last=0,cnt=0;
    for(register int i=1;i<=n;i++){
        if(a[i]-last<len) cnt++;
        else last=a[i];
    }
    if(cnt<=m) return true;
    else return false;
}
int main(){
    scanf("%d%d%d",&L,&n,&m);
    for(register int i=1;i<=n;i++) scanf("%d",&a[i]);
    a[++n]=L;
    lf=0,rg=L+2;
    while(lf<rg){
       mid=(lf+rg)>>1;
       if(check(mid)) ans=mid,lf=mid+1;
       else rg=mid;
    }
    printf("%d",ans);
}

第二道题:
2 . 子串
(substring.cpp/c/pas)
【问题描述】
有两个仅包含小写英文字母的字符串 A 和 B。 现在要从字符串 A 中取出 k 个 互不重
叠 的非空子串, 然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一
个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出
的位置不同也认为是不同的方案 。
【输入格式】
输入文件名为 substring.in。
第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问
题描述中所提到的 k,每两个整数之间用一个空格隔开。
第二行包含一个长度为 n 的字符串,表示字符串 A。
第三行包含一个长度为 m 的字符串,表示字符串 B。
【输出格式】
输出文件名为 substring.out。
输出共一行,包含一个整数,表示所求方案数。 由于答案可能很大,所以这里要求输
出答案对 1,000,000,007 取模 的结果。
【输入输出样例 1】
substring.in substring.out
6 3 1
aabaab
aab
2
见选手目录下 substring/substring1.in 与 substring/substring1.ans。
【输入输出样例 2】
substring.in substring.out
6 3 2
aabaab
aab
7
见选手目录下 substring/substring2.in 与 substring/substring2.ans。
【输入输出样例 3】
substring.in substring.out
6 3 3
aabaab
aab
7
见选手目录下 substring/substring3.in 与 substring/substring3.ans。
全国信息学奥林匹克联赛(NOIP2015)复赛 提高组 day2
第 4 页共 6 页
【输入输出样例说明】
所有合法方案如下: (加下划线的部分表示取出的子串)
样例 1:aab aab / aab aab
样例 2:a ab aab / a aba ab / a a ba ab / aab a ab
aa b aab / aa baa b / aab aa b
样例 3:a a b aab / a a baa b / a ab a a b / a aba a b
a a b a a b / a a ba a b / aab a a b
【输入输出样例 4】
见选手目录下 substring/substring4.in 与 substring/substring4.ans。
【数据规模与约定】
对于第 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。

题解:
这道题说难不难,说简单也不简单,找了一篇我觉得最好理解的题解附在这里,希望大家能够明白:
https://blog.sengxian.com/solutions/noip-2015-day2

#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1005;
const int mm=201;
const int mod=1000000007;
int n,m,K,s[maxn][mm],f[maxn][mm];
char A[maxn],B[maxn];
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>0;j--)
       if(A[i-1]==B[j-1]){
          for(int k=min(K,j);k>0;k--){
              f[j][k]=(s[j-1][k-1]+f[j-1][k])%mod;
              s[j][k]=(s[j][k]+f[j][k])%mod;
          }
       }else fill(f[j],f[j]+min(K,j)+1,0);
    printf("%d",s[m][K]);
} 

第三题:
3. 运输计划
(transport.cpp/c/pas)
【问题描述】
公元 2044 年,人类进入了宇宙纪元。
L 国有 n 个星球,还有 n-1 条 双向 航道,每条航道建立在两个星球之间,这 n-1 条
航道 连通 了 L 国的所有星球。
小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物
流飞船需要从 u i 号星球沿 最快 的宇航路径飞行到 v i 号星球去。 显然, 飞船驶过一条航道
是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 t j ,并且任意两艘飞船之
间 不会 产生任何干扰。
为了鼓励科技创新,L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小
P 把某一条航道改造成虫洞,飞船驶过虫洞 不消耗 时间。
在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,
这 m 个运输计划会 同时 开始,所有飞船 一起 出发。当这 m 个运输计划 都完成 时,小 P 的
物流公司的阶段性工作就完成了。
如果小 P 可以 自由选择 将哪一条航道改造成虫洞,试求出小 P 的物流公司完成阶段
性工作所需要的最短时间是多少?
【输入格式】
输入文件名为 transport.in。
第一行包括两个正整数 n、 m, 表示 L 国中星球的数量及小 P 公司预接的运输计划的
数量,星球从 1 到 n 编号。
接下来 n-1 行描述航道的建设情况,其中第 i 行包含三个整数 a i , b i 和 t i ,表示第
i 条双向航道修建在 a i 与 b i 两个星球之间,任意飞船驶过它所花费的时间为 t i 。
接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 u j 和 v j ,表示第 j 个
运输计划是从 u j 号星球飞往 v j 号星球。
【输出格式】
输出文件名为 transport.out。
共 1 行,包含 1 个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。
【输入输出样例 1】
transport.in transport.out
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
11
见选手目录下的 transport/transport1.in 与 transport/transport1.ans
全国信息学奥林匹克联赛(NOIP2015)复赛 提高组 day2
第 6 页共 6 页
【输入输出样例 1 说明】
将第 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。
【样例输入输出 2】
见选手目录下的 transport/transport2.in 与 transport/transport2.ans。
【数据规模与约定】
所有测试数据的范围和特点如下表所示
测试点编号 n= m= 约定
1
100
1
2 100 第 i 条航道连接 i 号星球与 i+1 号星球
3
4 2000 1
5 1000 1000
第 i 条航道连接 i 号星球与 i+1 号星球 6 2000 2000
7 3000 3000
8 1000 1000
9 2000 2000
10 3000 3000
11 80000 1
12 100000
13 70000 70000
第 i 条航道连接 i 号星球与 i+1 号星球 14 80000 80000
15 90000 90000
16 100000 100000
17 80000 80000
18 90000 90000
19 100000 100000
20 300000 300000
所有数据 1≤a i ,b i, u j ,v j ≤n,0≤t i ≤1000
请注意常数因子带来的程序效率上的影响。

题解:
这道题你要是会树上差分的话,那就很简单.
lca:最近公共祖先
从u到lca在从lca到v就是u到v之间的链.
这里简单讲一下树上差分.假如说有多条链,我们想找一条边被经过了多少次,我们只需要在每条链给出的时候,把起点和终点的vv值+1,lca值-2(vv是自己开的数组).最后dfs一次,把每个点加上自己儿子的vv值,每个点的vv最终就是自己这个点到father这条边被经过的次数.
为什么是正确呢?
设一条链从u到v,则u/v到lca都被遍历了一次,那么加上自己的子树和就能懂得了,u/v本身值+1,那u/v以上的因为加上自己的子树也会+1,当dfs回归到了lca的时候,因为减了2,所以左子树和右子树一共加的2就抵消了,那lca以上的即不属于这条链的点的vv就不会受到影响.若u,v的lca就是u,v之一呢,那么相当于就u,v一个+1,一个-1,模拟一下也不影响——这等同于序列差分.

这道题我用的st表求lca.

这道题主要思想是二分答案,二分的就是最短时间.把所有原计划时间>mid的计划统计出来,把这些计划的链差分,因为这些计划是不符合的,所以它们一定要经过黑洞,假设这些计划有cnt个,那么我们找到
被经过cnt次的边,在这些符合条件的边找出耗时最大的边作为黑洞,要是这些计划中的最大耗时的计划减去这个最大的黑洞那条边的耗时,要是<=mid的话,那这个mid就是成立的.
最后我多check了一下答案的前几项以免二分中容易出现与答案相差很小的自己的答案.若你二分的好的话也不必这样,反正我没这么干wa了一两个点.

#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=300001;
int st[maxn*2][20],seq[maxn*2],in[maxn],h[maxn],x,y,t,num,indexx,dis[maxn],ww[maxn],n,m,lca[maxn],vv[maxn],mid,cnt,maxx,mercer,lf,rg,ed,ans[maxn],aa[maxn],bb[maxn];
inline const int read(){
    register int f=1,x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return f*x;
}
struct edge{
    int v,nxt,w;
}e[maxn*2]; 
inline void add(int u,int v,int w){
    e[++num].v=v;
    e[num].nxt=h[u];
    e[num].w=w;
    h[u]=num;
}
void dfs(int u,int fa){
    seq[++indexx]=u;
    in[u]=indexx;
    for(int i=h[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa) continue;
        dis[v]=dis[u]+e[i].w;
        ww[v]=e[i].w;
        dfs(v,u);
        seq[++indexx]=u;
    }
}
inline void stha(){
    for(register int i=1;i<=indexx;i++) st[i][0]=in[seq[i]];
    for(int j=1;(1<<j)<indexx;j++)
     for(int i=1;i+(1<<j)-1<=indexx;i++)
      st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline int query(int L,int R){
    if(L>R) swap(L,R);
    int len=R-L+1;
    int k=0,ans;
    while((1<<(k+1))<len) k++;
    ans=min(st[L][k],st[R-(1<<k)+1][k]);
    return seq[ans];
}
void dfss(int u,int fa){
    for(int i=h[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa) continue;
        dfss(v,u);
        vv[u]+=vv[v];
    }
    if(vv[u]==cnt) mercer=max(mercer,ww[u]);
}
bool check(int mm){
    memset(vv,0,sizeof(vv));
    maxx=-1,cnt=0,mercer=0;
    for(int i=1;i<=m;i++) if(ans[i]>mm) vv[aa[i]]++,vv[bb[i]]++,vv[lca[i]]-=2,maxx=max(maxx,ans[i]),cnt++;;
    dfss(1,1);
    return maxx-mercer<=mm;
}
int main(){
    n=read(),m=read();
    for(register int i=1;i<n;i++) x=read(),y=read(),t=read(),add(x,y,t),add(y,x,t);
    dfs(1,1);
    stha();
    for(register int i=1;i<=m;i++){
        aa[i]=read(),bb[i]=read();
        lca[i]=query(in[aa[i]],in[bb[i]]);
        ans[i]=dis[aa[i]]+dis[bb[i]]-dis[lca[i]]*2;
        maxx=max(maxx,ans[i]);
    }
    lf=0,rg=maxx+1;
    while(rg-lf>1){
        mid=(lf+rg)>>1;
        if(check(mid)) ed=mid,rg=mid-1;
        else lf=mid+1;
    }
    while(check(ed-1)) ed--; 
    printf("%d",ed);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值