NOIP2016 提高组 解题报告

说明:由于我能力的限制,文章中的做法不一定是最优秀的算法,但官方数据全部测试通过,使用的全部知识全部是NOIP提高组的知识,请组织放心查看。
感谢 GoodQt 的指导与帮助


DAY1 T1 玩具谜题

题目来源:洛谷 1563
吐槽:

对NOIP出题人“魔法师”英语单词拼错表示(滑稽)。

思路:

模拟操作过程,分类讨论即可。
时间复杂度: O(n+m)

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node{
    int dir;
    char name[15];
};
node p[100010];
int n, m;
int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++) scanf("%d%s", &p[i].dir, p[i].name+1);
    int now = 1;
    for(int i = 1; i <= m; i ++){
        int a, b;
        scanf("%d%d", &a, &b);
        if(p[now].dir == 0){
            if(a == 0) now = ((now - b) % n + n) % n;
            else now = (now + b) % n;
        }else{
            if(a == 0) now = (now + b) % n;
            else now = ((now - b) % n + n) % n;
        }
        if(now == 0) now = n;
    }
    printf("%s", p[now].name+1);
    return 0;
}

DAY1 T2 天天爱跑步

题目来源:洛谷 1600
吐槽:

这题的正解好神啊,巨坑,说好的NOIP按难度顺序出题呢?

思路:

读入第i个点的观察时间是 val[i]
通过DFS预处理,可以预处理得到每个节点的深度deep[i]和倍增需要的 up[Max][MaxLog] 数组,用倍增求得lca,在求得lca以后,我们可以把在lca路径上的点的操作转化为从起点和终点分别到根节点的操作-lca到根节点的操作-lca的父节点到根节点的操作。这样对于每一个跑步者的一个操作,我们把它分成了4个部分。
定义对与第 i 组询问,是从u[i] v[i] 的路径,路径长度是 len[i] ,lca是 lca[i]
具体的来说我们把问题转换成了从一个点 x 到根节点的路径中,所有满足deep[x]+val[x]==deep[u[i]]或者 deep[x]val[x]==deep[v[i]]len[i] 的点的值加1或者减1。
不难发现,一个节点 x 的答案被增加,只有可能受到以x为根的子树中某个点的影响。
而且需要满足的增加条件是定值(就是deep[u[i]] deep[v[i]]len[i] ),然后就可以开一个桶,维护定值出现的次数,用邻接链表储存这些定值,根据dfs的性质(只有当子树全部访问完以后才会回到根),在访问子树之前记录下当前节点的定值,在操作完子树,维护了自己本身的节点以后,直接像前缀和一样,把新的值与操作前的值相减就好了。
时间复杂度: O(nlogn+n)
(时间的瓶颈在求lca,lca如果用tarjan离线求可以做到并查集复杂度 O(αn)

代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 300010;
struct node{
    int begin, end, lca, len;
};
node p[maxn];
int n, m, val[maxn];
int _first[maxn], _next[maxn*2], _to[maxn*2], cnt;
int ins1[maxn], ins2[maxn], del1[maxn], del2[maxn];
int nxt[maxn*4], v[maxn*4], cnt1;
int deep[maxn], up[maxn][20];
int ans[maxn];
int ct1[maxn*2], ct2[maxn*2];
inline void add1(int a, int b, int c){
    nxt[++cnt1] = ins1[a];
    v[ins1[a] = cnt1] = c;
    nxt[++cnt1] = del1[b];
    v[del1[b] = cnt1] = c;
}
inline void add2(int a, int b, int c){
    nxt[++cnt1] = ins2[a];
    v[ins2[a] = cnt1] = c;
    nxt[++cnt1] = del2[b];
    v[del2[b] = cnt1] = c;
}
inline void add(int a, int b){
    _next[++cnt] = _first[a];
    _to[_first[a] = cnt] = b;
}
void dfs(int x){
    for(int i = 1; i <= 18; i ++){
        up[x][i] = up[up[x][i-1]][i-1];
    }
    for(int i = _first[x]; i; i = _next[i]){
        if(_to[i] == up[x][0]) continue;
        up[_to[i]][0] = x, deep[_to[i]] = deep[x]+1;
        dfs(_to[i]);
    }
}
void lca(){
    for(int ii = 1; ii <= m; ii ++){
        int x = p[ii].begin, y = p[ii].end;
        if(deep[x] < deep[y]) swap(x, y);
        if(deep[x] != deep[y])
            for(int i = 18; i >= 0; i --)
                if(deep[up[x][i]] >= deep[y]) x = up[x][i];
        if(x != y){
            for(int i = 18; i >= 0; i --)
                if(up[x][i] != up[y][i]) x = up[x][i], y = up[y][i];
            x = up[x][0];
        }
        p[ii].lca = x;
        p[ii].len = deep[p[ii].begin] - deep[x] + deep[p[ii].end] - deep[x];
    }
}
inline void dfs1(int x){
    ans[x] = - ct1[deep[x]+val[x]] - ct2[deep[x]-val[x]+n];
    for(int i = _first[x]; i; i = _next[i]){
        if(_to[i] == up[x][0]) continue;
        dfs1(_to[i]);
    }
    for(int i = ins1[x]; i; i = nxt[i]) ct1[v[i]] ++;
    for(int i = del1[x]; i; i = nxt[i]) ct1[v[i]] --;
    for(int i = ins2[x]; i; i = nxt[i]) ct2[v[i]+n] ++;
    for(int i = del2[x]; i; i = nxt[i]) ct2[v[i]+n] --;
    ans[x] += ct1[deep[x]+val[x]] + ct2[deep[x]-val[x]+n];
}
int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i < n; i ++){
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }
    deep[1] = 1, dfs(1);
    for(int i = 1; i <= n; i ++) scanf("%d", &val[i]);
    for(int i = 1; i <= m; i ++) scanf("%d%d", &p[i].begin, &p[i].end); 
    lca();
    for(int i = 1; i <= m; i ++){
        add1(p[i].begin, up[p[i].lca][0], deep[p[i].begin]);
        add2(p[i].end, p[i].lca, deep[p[i].end]-p[i].len);
    }
    dfs1(1);
    for(int i = 1; i < n; i ++) printf("%d ", ans[i]); printf("%d", ans[n]);
    return 0;
}

DAY1 T3 换教室

题目来源:洛谷 1850
吐槽:

NOIP有史以来第一次考期望,对吧。

思路:

对于期望的概念,举例来说,已知A转换为B的代价为 p1 ,现在有0.6的可能性让A转换为B的代价变为 p2 ,那么A转换为B的期望代价就是 p10.4+p20.6 。进行期望计算的时候,一定要保证所有的项出现的概率之和为1。上面的例子中, 0.4+0.6=1
对于这个题,我们可以先用Floyd预处理每两个点之间的最短路,这样走一定是最优的。
剩下的部分可以用动态规划来实现,设dp[i][j][0/1]表示考虑前 i 个物品,已经换了j个的最小期望代价。
状态转移方程:
(太长了,好难看,看程序里的吧 ^_^)
其中 dis[i][j] 表示从 i j的最短路, w[i] 表示换成功的机率。
时间复杂度: O(e3+nm)

代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int inf = 2e9;
int n, m, v, e;
int a1[2010], a2[2010];
double dp[2010][2010][2], dis[310][310], w[2010], ans;
int main(){
    scanf("%d%d%d%d", &n, &m, &v, &e);
    for(int i = 1; i <= n; i ++) scanf("%d", &a1[i]);
    for(int i = 1; i <= n; i ++) scanf("%d", &a2[i]);
    for(int i = 1; i <= n; i ++) scanf("%lf", &w[i]);
    for(int i = 0; i <= v; i ++)
        for(int j = 0; j <= v; j ++)
            if(i == j) dis[i][j] = 0;
            else dis[i][j] = inf;
    for(int i = 1; i <= e; i ++){
        int a, b; double c;
        scanf("%d%d%lf", &a, &b, &c);
        dis[a][b] = min(dis[a][b], c);
        dis[b][a] = min(dis[b][a], c);
    } 
    for(int k = 1; k <= v; k ++)
        for(int i = 1; i <= v; i ++)
            for(int j = 1; j <= v; j ++)
                if(dis[i][j] > dis[i][k]+dis[k][j])
                    dis[i][j] = dis[i][k]+dis[k][j];
    for(int i = 0; i <= n; i ++)
        for(int j = 0; j <= m; j ++)
            dp[i][j][0] = dp[i][j][1] = inf;
    dp[1][0][0] = 0;
    dp[1][1][1] = 0;
    for(int i = 2, up; i <= n; i ++){
        up = min(i, m);
        for(int j = 0; j <= up; j ++){
            dp[i][j][0] = min(dp[i][j][0], dp[i-1][j][0]
                        +dis[a1[i]][a1[i-1]]);
            dp[i][j][0] = min(dp[i][j][0], dp[i-1][j][1]
                        +dis[a1[i]][a2[i-1]]*w[i-1]
                        +dis[a1[i]][a1[i-1]]*(1-w[i-1]));
            if(j-1 >= 0) dp[i][j][1] = min(dp[i][j][1], dp[i-1][j-1][0]
                        +dis[a2[i]][a1[i-1]]*w[i]
                        +dis[a1[i]][a1[i-1]]*(1-w[i]));
            if(j-1 >= 0) dp[i][j][1] = min(dp[i][j][1], dp[i-1][j-1][1]
                        +dis[a2[i]][a2[i-1]]*w[i]*w[i-1]
                        +dis[a2[i]][a1[i-1]]*w[i]*(1-w[i-1])
                        +dis[a1[i]][a2[i-1]]*(1-w[i])*w[i-1]
                        +dis[a1[i]][a1[i-1]]*(1-w[i])*(1-w[i-1]));
        }
    }
    ans = inf;
    for(int i = 0; i <= m; i ++) ans = min(ans, min(dp[n][i][0], dp[n][i][1]));
    printf("%.2lf", ans);
    return 0;
} 

DAY2 T1 组合数问题

题目来源:洛谷 2822
吐槽:

果然数学题隔一年出一次。我竟然因为开小数组挂了。

思路:

预处理杨辉三角,每次转移时把相加后的值对 k 取模,结果用二维前缀和记录。
时间复杂度:O(nm+T)

代码:
#include <cstdio>
#include <iostream>
using namespace std;
int T, k;
int sum[2010][2010], val[2010][2010];
int a[10010], b[10010], p1, p2;
int main(){
    scanf("%d%d", &T, &k);
    for(int i = 1; i <= T; i ++){
        scanf("%d%d", &a[i], &b[i]);
        p1 = max(p1, a[i]);
        p2 = max(p2, b[i]);
    }
    for(int i = 0; i <= p1; i ++){
        for(int j = 0; j <= min(p2, i); j ++){
            if(j == 0){val[i][j] = 1;continue;}
            val[i][j] = (val[i-1][j] + val[i-1][j-1]) % k;
            if(val[i][j] == 0) sum[i][j] = 1;
        }
    }
    for(int i = 1; i <= p1; i ++){
        for(int j = 1; j <= p2; j ++){
            sum[i][j] = sum[i-1][j] + sum[i][j-1] + sum[i][j] - sum[i-1][j-1];
        }
    }
    for(int i = 1; i <= T; i ++){
        printf("%d\n", sum[a[i]][b[i]]);
    }
    return 0;
} 

DAY2 T2 蚯蚓

题目来源:洛谷 2827
吐槽:

论巨慢的CCF评测机与巨慢的STL优先队列。

思路:

因为每一次蚯蚓的长度都会增加,而时间又不允许把所有的长度都进行增减,考虑到我们只需要知道所有蚯蚓的相对大小关系,所以把剩余的打标记,把新切出来的减去其余增加的长度,模拟每次操作。
如果用堆来维护大小的话,时间复杂度为 O((n+m)log(n+m)) 实测手写堆65分,优先队列60分。
考虑优化。每次取出来切的蚯蚓一定是最长的一个,切完之后的新生成的蚯蚓不会比原来的蚯蚓长。基于这一点,我们考虑优化。如果我们现将输入的 n 个数按长度排序,然后把新生成的两个小的插入到输入的这n个元素序列中,时间复杂度是 O(n) 的,思考归并排序的思想,因为取出来分割的比例是固定的,所以比较长的分割出来的新蚯蚓的长度,一定比较短的分割出来的新蚯蚓的长度要长。所以说有单调性,然后就可以像归并排序一样维护最大值了。
具体的,我们开两个队列(拒绝STL),分别维护每次分割完以后较长的一段和较短的一段的长度,加上输入序列,三个数列全部满足单调性。每次要切的蚯蚓从输入数列中和新开的两个队列中取最大值。实现的细节是要注意队列的边界,实现请参考代码。
时间复杂度: O(nlogn+(n+m))

代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAX(a,b) (((a)>(b))?(a):(b))
#define MIN(a,b) (((a)<(b))?(a):(b))
using namespace std;
inline int gt(){  
    char _ch = ' ';  
    int _num = 0, _op = 1, _ok = 0;  
    while(1){
        _ch = getchar();
        if(_ch == '-') _op *= -1;  
        else if(_ch >= '0' && _ch <= '9') _num = _num*10 + _ch - '0', _ok = 1;  
        else if(_ok) return _op * _num; 
    } 
}  
int n, m, q, u, v, t;
int left1;
int len[100010], q1[8000010], q2[8000010];
int out[1000010], cnt;
int main(){
    scanf("%d%d%d%d%d%d", &n, &m, &q, &u, &v, &t);
    for(int i = 1; i <= n; i ++) len[i] = gt();
    sort(len+1, len+1+n);
    int p1 = 1, p2 = 0, p3 = 1, p4 = 0, p5 = n, now = 0;
    for(int i = 1; i <= m; i ++){
        if(p5 >= 1 && len[p5] >= MAX(q1[p1], q2[p3])) now = len[p5--] + left1;
        else if(p1 <= p2 && q1[p1] >= q2[p3]) now = q1[p1++] + left1;
        else if(p3 <= p4) now = q2[p3++] + left1;
        if(i%t == 0) out[++cnt] = now;
        left1 += q;
        int new1 = ((long long)now*(long long)u)/(long long)v;
        int new2 = now - new1;
        q1[++p2] = MAX(new1, new2) - left1;
        q2[++p4] = MIN(new1, new2) - left1;
    }
    for(int i = 1; i <= cnt; i ++) printf("%d ", out[i]);
    printf("\n");
    for(int i = 1, w = 0; i <= n+m; i ++, w = 0){
        if(p5 >= 1 && len[p5] >= MAX(q1[p1], q2[p3])) w = len[p5--] + left1;
        else if(p1 <= p2 && q1[p1] >= q2[p3]) w = q1[p1++] + left1;
        else if(p3 <= p4) w = q2[p3++] + left1;
        if(i%t != 0) continue;
        printf("%d ", w);
    }
    return 0;
}

DAY2 T3 愤怒的小鸟

题目来源:洛谷 2831
吐槽:

我在考前猜对了游戏,哈哈哈。正解是状压DP,又是第一次?

思路:

状态压缩动态规划。首先用18位二进制数表示,每一个是否被打了。
因为一直一定过原点,所以再有两个点就可以确定。
设另外的两个点为 A(x1,y1),B(x2,y2)
设抛物线的方程为 y=ax2+bx
带入方程:
y1=ax21+bx1
y2=ax22+bx2
上面乘 x2 ,下面乘 x1
y1x2=ax21x2+bx1x2
y2x1=ax22x1+bx1x2
上式减下式:
y1x2y2x1=a(x21x2x22x1)
解得:
a=y1x2y2x1x21x2x22x1
b=y1ax21x1
应该满足的条件是: a<0
然后就可以预处理出 num[i][j] 表示以 i 为第一个点的第j种方案。
然后就是动态规划了,设 dp[i] 表示现在的攻击状态二进制表示为 i 的最小次数。
dp[i|num[now][j]]=min(dp[i|num[now][j]],dp[i]+1)
dp[i|(1 << (now1))]=min(dp[i|(1 << (now1))],dp[i]+1)
其中, now i 这个状态中没有被攻击的最低的一位,因为考虑到在最优的答案中,这个位上一定会被攻击,所以只需要枚举攻击经过第now位的即可,此时做多有 n 种可能。
对于选定的第now位来说,也可以选择只打这一个,即为第二个方程。
时间复杂度: O(Tn2n)

代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 0.000000001;
struct point{
    double x, y;
};
int T, n, m, ans = 1e9;
point p[25];
int num[25][25], dp[300010];
int calc(double a, double b){
    int res = 0, now = 1;
    for(int i = 1; i <= n; i ++, now <<= 1){
        double t = a*p[i].x*p[i].x+b*p[i].y;
        if(abs(a*p[i].x*p[i].x+b*p[i].x-p[i].y) < eps){
            res |= now;
        }
    }
    return res;
}
int bits(int x){
    int res = 0;
    for(int i = 1; i <= n; i ++, x >>= 1) if((x&1) == 0) res ++;
    return res;
}
int main(){
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i ++){
            scanf("%lf%lf", &p[i].x, &p[i].y);
        }
        memset(dp, 63, sizeof(dp));
        memset(num, 0, sizeof(num));
        ans = 1e9;
        for(int i = 1; i <= n; i ++){
            for(int j = i+1; j <= n; j ++){
                if(i == j) continue;
                if(abs(p[i].x-p[j].x) < eps) continue;
                double a = (p[i].y*p[j].x-p[j].y*p[i].x)/(p[i].x*p[i].x*p[j].x-p[j].x*p[j].x*p[i].x);
                if(a >= 0) continue;
                double b = (p[i].y-a*p[i].x*p[i].x)/p[i].x;
                num[i][++num[i][0]] = calc(a,b);
            } 
        }
        int MAX = (1<<n)-1;
        dp[0] = 0;
        for(int i = 0; i <= MAX; i ++){
            int now = 0;
            for(int j = 1, w = 1; j <= n; j ++, w <<= 1){
                if((w&i) == 0){now = j;break;}
            }
            if(now == 0) continue;
            dp[i|(1<<(now-1))] = min(dp[i|(1<<(now-1))], dp[i] + 1);
            for(int j = 1; j <= num[now][0]; j ++){
                dp[i|num[now][j]] = min(dp[i|num[now][j]], dp[i] + 1);
            }
        }
        printf("%d\n", dp[MAX]);
    }
    return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOIP2011)复赛 提高 day2 1 页 共 4 页 全国信息学奥林匹克联赛(NOIP2011)复赛 提高 day2 (请选手务必仔细阅读本页内容) 一.题目概况 中文题目名称 计算系数 聪明的质监员 观光公交 英文题目与子目录名 factor qc bus 可执行文件名 factor qc bus 输入文件名 factor.in qc.in bus.in 输出文件名 factor.out qc.out bus.out 每个测试点时限 1 秒 1 秒 1 秒 测试点数目 10 20 20 每个测试点分值 10 5 5 附加样例文件 有 有 有 结果比较方式 全文比较(过滤行末空格及文末回车) 题目类型 传统 传统 传统 二.提交源程序文件名 对于 C++语言 factor.cpp qc.cpp bus.cpp 对于 C 语言 factor.c qc.c bus.c 对于 pascal 语言 factor.pas qc. Pas bus. pas 三.编译命令(不包含任何优化开关) 对于 C++语言 g++ -o factor factor.cpp -lm g++ -o qc qc.cpp –lm g++ -o bus bus.cpp -lm 对于 C 语言 gcc -o factor factor.c -lm gcc -o qc qc.c –lm gcc -o bus bus.c -lm 对于 pascal 语言 fpc factor.pas fpc qc.pas fpc bus.pas 四.运行内存限制 内存上限 128M 128M 128M 注意事项: 1、文件名(程序名和输入输出文件名)必须使用英文小写。 2、C/C++中函数 main()的返回值类型必须是 int,程序正常结束时的返回值必须是 0。 3、全国统一评测时采用的机器配置为:CPU P4 3.0GHz,内存 1G,上述时限以此配置为准。 4、特别提醒:评测在 NOI Linux 下进行。 NOIP2011)复赛 提高 day2 2 页 共 4 页 【问题描述】 1.计算系数 (factor.cpp/c/pas) 给定一个多项式 (ax + by)k ,请求出多项式展开后 x n y m 项的系数。 【输入】 输入文件名为 factor.in。 共一行,包含 5 个整数,分别为 a,b,k,n,m,每两个整数之间用一个空格隔开。 【输出】 输出文件名为 factor.out。 输出共 1 行,包含一个整数,表示所求的系数,这个系数可能很大,输出对 10007 取 模后的结果。 【输入输出样例】 factor.in factor.out 1 1 3 1 2 3 【数据范围】 对于 30%的数据,有 0≤k≤10; 对于 50%的数据,有 a = 1,b = 1; 对于 100%的数据,有 0≤k≤1,000,0≤n, m≤k,且 n + m = k,0≤a,b≤1,000,000。 【问题描述】 2.聪明的质监员 (qc.cpp/c/pas) 小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1 到 n 逐一编号,每个矿石都有自己的重量 wi 以及价值 vi。检验矿产的流程是: 1、给定 m 个区间[Li,Ri]; 2、选出一个参数 W; 3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值 Yi : Yi = ∑1* ∑v j , j ∈[Li , Ri ] 且 w j ≥ W ,j 是矿石编号 j j m 这批矿产的检验结果 Y 为各个区间的检验值之和。即:Y = ∑Yi i =1 若这批矿产的检验结果与所给标准值 S 相差太多,就需要再去检验另一批矿产。小 T 不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让检验结果尽可能的靠近 标准值 S,即使得 S-Y 的绝对值最小。请你帮忙求出这个最小值。 【输入】 输入文件 qc.in。 NOIP2011)复赛 提高 day2 3 页 共 4 页 第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。 接下来的 n 行,每行 2 个整数,中间用空格隔开,第 i+1 行表示 i 号矿石的重量 wi 和价 值 vi 。 接下来的 m 行,表示区间,每行 2 个整数,中间用空格隔开,第 i+n+1 行表示区间[Li, Ri]的两个端点 Li 和 Ri。注意:不同区间可能重合或相互重叠。 【输出】 输出文件名为 qc.out。 输出只有一行,包含一个整数,表示所求的最小值。 【输入输出样例】 qc.in qc.out 5 3 15 1 5 2 5 3 5 4 5 5 5 1 5 2 4 3 3 10 【输入输出样例说明】 当 W 选 4 的时候,三个区间上检验值分别为 20、5、0,这批矿产的检验结果为 25,此 时与标准值 S 相差最小为 10。 【数据范围】 对于 10%的数据,有 1≤n,m≤10; 对于 30%的数据,有 1≤n,m≤500; 对于 50%的数据,有 1≤n,m≤5,000; 对于 70%的数据,有 1≤n,m≤10,000; 对于 100%的数据,有 1≤n,m≤200,000,0 < wi, vi≤106,0 < S≤1012,1≤Li≤Ri≤n。 【问题描述】 3.观光公交 (bus.cpp/c/pas) 风景迷人的小城 Y 市,拥有 n 个美丽的景点。由于慕名而来的游客越来越多,Y 市特 意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第 0 分钟出现在 1 号景点,随后依次前往 2、3、4……n 号景点。从第 i 号景点开到第 i+1 号景点需要 Di 分钟。 任意时刻,公交车只能往前开,或在景点处等待。 设共有 m 个游客,每位游客需要乘车 1 次从一个景点到达另一个景点,第 i 位游客在 Ti 分钟来到景点 Ai,希望乘车前往景点 Bi(Ai<Bi)。为了使所有乘客都能顺利到达目的地, NOIP2011)复赛 提高 day2 4 页 共 4 页 公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。 假设乘客上下车不需要时间。 一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一 辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司 机 ZZ 给公交车安装了 k 个氮气加速器,每使用一个加速器,可以使其中一个 Di 减 1。对于 同一个 Di 可以重复使用加速器,但是必须保证使用后 Di 大于等于 0。 那么 ZZ 该如何安排使用加速器,才能使所有乘客的旅行时间总和最小? 【输入】 输入文件名为 bus.in。 第 1 行是 3 个整数 n, m, k,每两个整数之间用一个空格隔开。分别表示景点数、乘客数 和氮气加速器个数。 第 2 行是 n-1 个整数,每两个整数之间用一个空格隔开,第 i 个数表示从第 i 个景点开 往第 i+1 个景点所需要的时间,即 Di。 第 3 行至 m+2 行每行 3 个整数 Ti, Ai, Bi,每两个整数之间用一个空格隔开。第 i+2 行表 示第 i 位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。 【输出】 输出文件名为 bus.out。共一行,包含一个整数,表示最小的总旅行时间。 【输入输出样例】 bus.in bus.out 3 3 2 1 4 0 1 3 1 1 2 5 2 3 10 【输入输出样例说明】 对 D2 使用 2 个加速器,从 2 号景点到 3 号景点时间变为 2 分钟。 公交车在第 1 分钟从 1 号景点出发,第 2 分钟到达 2 号景点,第 5 分钟从 2 号景点出发, 第 7 分钟到达 3 号景点。 第 1 个旅客旅行时间 7-0 = 7 分钟。 第 2 个旅客旅行时间 2-1 = 1 分钟。 第 3 个旅客旅行时间 7-5 = 2 分钟。 总时间 7+1+2 = 10 分钟。 【数据范围】 对于 10%的数据,k=0; 对于 20%的数据,k=1; 对于 40%的数据,2 ≤ n ≤ 50,1 ≤ m ≤ 1,000,0 ≤ k ≤ 20,0 ≤ Di ≤ 10,0 ≤ Ti ≤ 500; 对于 60%的数据,1 ≤ n ≤ 100,1 ≤ m ≤ 1,000,0 ≤ k ≤ 100,0 ≤ Di ≤ 100,0 ≤ Ti ≤ 10,000; 对于 100% 的数据,1 ≤ n ≤ 1,000 ,1 ≤ m ≤ 10,000 ,0 ≤ k ≤ 100,000 ,0 ≤ Di ≤ 100 , 0 ≤ Ti ≤ 100,000。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值