Noip 模拟练习9

Posted bigyellowdog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Noip 模拟练习9相关的知识,希望对你有一定的参考价值。

Noip 模拟练习9

  • 较容易。一次AK。

锻炼计划

Description

  • 身体是革命的本钱, OIers 不要因为紧张的学习和整天在电脑前而忽视了健
    康问题。小 x 设计了自己的锻炼计划,但他不知道这个计划是否可行,换句话
    说如果计划不当可能会让他的体力超支,所以小 x 请你帮助他。
    一天有 1440 分钟,所以小 x 列出的是这一整天第 1 至第 1440 分钟的计划。
    小 x 的体力用一个整数来表示,他会按照计划表进行锻炼,同时,每分钟小 x
    的体力会自动增加 1。如果某一分钟末小 x 的体力小于等于零,那么可怜的小 x
    就累死了……

Input

  • 第一行是用空格分开的两个整数 n,m,分别表示小 x 的初始体力值和计划的
    项目数量。
    从第二行开始的 m 行,每行描述一个锻炼项目:名称、开始时间 a、结束时
    间 b、每分钟耗费的体力(用空格分隔),表示此项目从第 a 分钟初开始,第 b 分
    钟末结束。锻炼项目按照开始时间递增顺序给出,不会出现两个项目时间冲突
    的情况。

Output

  • 输出包括两行,如果计划可行,第一行输出"Accepted",第二行输出这一天
    过后最后剩余的体力;否则在第一行输出"Runtime Error",第二行输出在第几分
    钟累死。

Sample Input

Basketball 1 10 1

Sample output

Accepted

1140

题解:

  • 模拟... ...
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;

int n, m;
int a[2005];

int main()

    cin >> n >> m;
    for(int i = 1; i <= m; i++)
    
        string t; cin >> t;
        int u, v, w;
        cin >> u >> v >> w;
        for(int j = u; j <= v; j++) a[j] += w;
    
    for(int i = 1; i <= 1440; i++)
    
        n++, n -= a[i];
        if(n <= 0) cout << "Runtime Error\n" << i; return 0;
    
    cout << "Accepted\n" << n;
    return 0;

魔兽争霸

Description

  • 小 x 正在销魂地玩魔兽
    他正控制着死亡骑士和 n 个食尸鬼(编号 1~n)去打猎
    死亡骑士有个魔法,叫做“死亡缠绕”,可以给食尸鬼补充 HP
    战斗过程中敌人会对食尸鬼实施攻击,食尸鬼的 HP 会减少
    小 x 希望随时知道自己部队的情况,即 HP 值第 k 多的食尸鬼有多少 HP,
    以便决定如何施放魔法
    请同学们帮助他:)
    小 x 向你发出 3 种信号:(下划线在输入数据中表现为空格)
    A_i_a 表示敌军向第 i 个食尸鬼发出了攻击,并使第 i 个食尸鬼损失了 a 点
    HP ,如果它的 HP<=0, 那么这个食尸鬼就死了 (Undead 也是要死的)。
    敌军不会攻击一个已死的食尸鬼。
    C_i_a 表示死亡骑士向第 i 个食尸鬼放出了死亡缠绕,并使其增加了 a 点 HP。
    HP 值没有上限。
    死亡骑士不会向一个已死的食尸鬼发出死亡缠绕
    Q_k 表示小 x 向你发出询问

Input

  • 第一行,一个正整数 n
    以后 n 个整数 表示 n 个食尸鬼的初始 HP
    值接着一个正整数 m
    以下 m 行 每行一个小 x 发出的信号

Output

  • 对于小 x 的每个询问,输出 HP 第 k 多的食尸鬼有多少 HP,如果食尸鬼总
    数不足 k 个,输出-1。每个一行数。
    最后一行输出一个数:战斗结束后剩余的食尸鬼数

Sample Input

5

1 2 3

4 5

10

Q 2

A 4 6

C 1 4

Q 2

A 2 1

A 3 3

A 1 3

Q 4

C 2 10

Q 1

Sample output

4

5

-1

11

3

题解:

  • 平衡树裸题。我用fhq-treap实现。
  • 做法就是每次修改一个点就删除修改前的点,再加入修改后的点。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define N 30005
using namespace std;

struct T int l, r, val, dat, size; t[N * 4];
int n, m, root, x, y, z, tot, now;
int a[N];

int read()

    int x = 0; char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0'; c = getchar();
    return x;


int New(int val)

    t[++tot].val = val;
    t[tot].dat = rand();
    t[tot].size = 1;
    return tot;


void up(int p) t[p].size = t[t[p].l].size + t[t[p].r].size + 1;

void split(int p, int val, int &x, int &y)

    if(!p) x = y = 0; return;
    if(t[p].val <= val) x = p, split(t[p].r, val, t[p].r, y);
    else y = p, split(t[p].l, val, x, t[p].l);
    up(p);


int merge(int x, int y)

    if(!x || !y) return x + y;
    if(t[x].dat > t[y].dat)
    
        t[x].r = merge(t[x].r, y);
        up(x); return x;
    
    else
    
        t[y].l = merge(x, t[y].l);
        up(y); return y;
    


void insert(int val)

    split(root, val - 1, x, y);
    root = merge(merge(x, New(val)), y);


void erase(int val)

    split(root, val, x, z);
    split(x, val - 1, x, y);
    y = merge(t[y].l, t[y].r);
    root = merge(merge(x, y), z);


int valOfRank(int rank)

    int p = root;
    while(p)
    
        if(t[t[p].l].size + 1 == rank) break;
        else if(t[t[p].l].size >= rank) p = t[p].l;
        else rank -= t[t[p].l].size + 1, p = t[p].r;
    
    return t[p].val;


int main()

    cin >> n, now = n;
    for(int i = 1; i <= n; i++)
    
        a[i] = read();
        insert(a[i]);
    
    cin >> m;
    for(int i = 1; i <= m; i++)
    
        char c[3]; scanf("%s", c);
        if(c[0] == 'A')
        
            int pos = read(), val = read();
            erase(a[pos]);
            a[pos] -= val;
            if(a[pos] <= 0) now--;
            else insert(a[pos]);
        
        else if(c[0] == 'C')
        
            int pos = read(), val = read();
            erase(a[pos]);
            a[pos] += val;
            insert(a[pos]);
        
        else if(c[0] == 'Q')
        
            int rank = read();
            if(now < rank) printf("-1\n");
            else printf("%d\n", valOfRank(now - rank + 1));
        
    
    cout << now;
    return 0;

暗黑破坏神

Description

  • 无聊中的小 x 玩起了 Diablo I...
    游戏的主人公有 n 个魔法
    每个魔法分为若干个等级,第 i 个魔法有 p[i]个等级(不包括 0)
    每个魔法的每个等级都有一个效果值,一个 j 级的 i 种魔法的效果值为
    w[i][j] 魔法升一级需要一本相应的魔法书
    购买魔法书需要金币,第 i 个魔法的魔法书价格为 c[i]
    而小 x 只有 m 个金币(好孩子不用修改器)
    你的任务就是帮助小 x 决定如何购买魔法书才能使所有魔法的效果值之和最

    开始时所有魔法为 0 级 效果值为 0

Input

  • 第一行 用空格隔开的两个整数 n m
    以下 n 行 描述 n 个魔法
    第 i+1 行描述 第 i 个魔法 格式如下
    c[i] p[i] w[i][1] w[i][2] ... w[i][p[i]]

Output

  • 第一行输出一个整数,即最大效果值。
    以后 n 行输出你的方案:
    第 i+1 行有一个整数 v[i] 表示你决定把第 i 个魔法学到 v[i]级
    如果有多解 输出花费金币最少的一组
    如果还多解 输出任意一组

Sample Input

3 10

1 3 1 2 2

2 3 2 4 6

3 3 2 1 10

Sample output

11

1

0

3

题解:

  • 线性dp。
  • 设dp(i, j)为处理了前i个魔法,用j个金币获得的最大效果值。
  • 通过上一个(i - 1)物品的状态,然后依次取当前物品的不同形态取最优值。
  • 比较容易,主要是输出方案。
  • 想了一下我用a(i, j)同步记录当算到dp(i, j)时,得到dp(i, j)时,用的是哪个物品。
  • ok,搞定。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 2005
using namespace std;

struct Ans int id, lev; ans[N];
struct A int val, id, lev; a[N][N];
int n, T;
int cnt[N];
int w[N][N], v[N][N], dp[N][N];

bool cmp(Ans x, Ans y) return x.id < y.id;

int main()

    cin >> n >> T;
    for(int i = 1; i <= n; i++)
    
        int c; cin >> c;
        int p; cin >> p;
        cnt[i] = p;
        for(int j = 1; j <= p; j++)
            cin >> v[i][j], w[i][j] = c * j;
    
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= T; j++)
            for(int k = 0; k <= cnt[i]; k++)
                if(j - w[i][k] >= 0)
                    if(dp[i - 1][j - w[i][k]] + v[i][k] > dp[i][j])
                    
                        dp[i][j] = dp[i - 1][j - w[i][k]] + v[i][k];
                        a[i][j].id = i, a[i][j].lev = k;
                        a[i][j].val = w[i][k];
                    
                    else if(dp[i - 1][j - w[i][k]] + v[i][k] == dp[i][j] && w[i][k] < a[i][j].val)
                    
                        dp[i][j] = dp[i - 1][j - w[i][k]] + v[i][k];
                        a[i][j].id = i, a[i][j].lev = k;
                        a[i][j].val = w[i][k];
                    
    cout << dp[n][T] << endl;
    int now = T;
    for(int i = n; i >= 1; i--)
    
        ans[i].id = a[i][now].id;
        ans[i].lev = a[i][now].lev;
        now = now - a[i][now].val;
    
    sort(ans + 1, ans + 1 + n, cmp);
    for(int i = 1; i <= n; i++) cout << ans[i].lev << endl;
    return 0;

以上是关于Noip 模拟练习9的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2017练习跳跃切除子序列(模拟)

9.2NOIP模拟题

NOIP模拟17.9.22

2017-9-26 NOIP模拟赛

NOIP模拟17.9.21

NOIP模拟9.17(TYVJNOIP2017模拟赛D2)