Noip 模拟练习3

Noip 模拟练习3

  • 满分300,本人100,修正后300
  • 较易

题目:

  • 有图,转链接

题解:

  • 其实这就是一个简单的卡特兰数。
  • 吐槽,这是原题
  • 证明过程转我写过的一篇博客
#include 
#include 
using namespace std;

int n;
int dp[25];

int main()
{
    cin >> n;
    dp[0] = dp[1] = 1;
    for(int i = 2; i <= n; i++)
        for(int j = 0; j < i; j++)
            dp[i] += dp[j] * dp[i - j - 1];
    cout << dp[n];
    return 0;
}

报名查询

Description

  • 某市中考科目繁多,且记分方法特别,以至于总分最高可达 10的6次方。为了公平起见,教育局规定成绩公布后两天为报名时间,考生可以根据自己的成绩选择填报某所学校。

    报名截止后,各校会将填报本校学生的分数从高到低排列,然后按招生计划指定的人数进行

    录取,因此如果志愿填报不合理你就可能落选。为了方便考生及时了解报名情况,该市最牛

    的 110 中学请你开发一个报名辅助软件,该软件主要有四个操作:

    (1) 投档:操作符为“I”,用以处理某位考生报考本校;

    (2) 查询:操作符为“C”,用以处理某位考生查询当前有多少考生总分比自己高;

    (3) 退档:操作符为“D”,用以处理某位考生退出报考本校;

    (4) 截止:操作符为“E”,表示报名截止,软件停止工作。

    在前三个操作中,每个操作符后都会跟有一个分数,操作符和分数之间用一个空格分开。

    如“I 10000”,表示有一名总分是 10000 的考生报考本校;“C 10000”表示当前有一名总分

    是 10000 的考生进行查询,此时程序应返回当前报考本校的考生中有多少个总分超过 10000;

    “D 10000”表示有一名总分是 10000 的考生退出报考本校。

    你的程序应该对每个投档和退档的操作对考生数据库进行实时更新,对每个查询操作输

    出正确的解答。

Input

  • 输入文件 query.in 每行给出一个操作,最后一行是操作符“E”表示输入结束。

    输入中每个操作符“I”、“C”、“D”后都跟有一个不超过 10的6次方

    的正整数,表示参与该次操作

    的考生总分。所有操作的总数不超过 10的6次方

Output

  • 对于输入文件中的每个查询操作“C”,必须在输出文件中给出正确的回答。

Sample Input

I 10000

I 20000

C 20000

I 10000

C 10000

I 5000

D 10000

C 5000

E

Sample output

0

1

2

题解:

  • 线段树 + 离散化
  • 复杂度是可以接受的,如果树的深度是21亿,找一个结点只需30几下。所以logn的复杂度是十分优秀的。何况这里的数据最大才10的6次方。
  • 区间修改 + 单点查询
  • 如果有一个分数为x的学生投档,那么比x低的分数那一段区间+1。退档即-1。
  • 查询直接查询那个分数位置即可。
  • 坑点:查询的成绩可能在之前并没有出现过而且大小随机
#include 
#include 
#include 
#include 
#define maxn 1000005
using namespace std;

struct Tree {int l, r, val, tag;}tree[maxn * 4];
struct A {int op, num;} a[maxn];
int n, cnt;
int b[maxn];

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 find(int x) {
    return upper_bound(b + 1, b + 1 + cnt, x) - b - 1;
    //注意查询的元素是不一定出现且随机的
}

void build(int root, int l, int r)
{
    tree[root].l = l, tree[root].r = r;
    if(l == r) return;
    int mid = (l + r) >> 1;
    build(root << 1, l, mid);
    build(root << 1 | 1, mid + 1, r);
}

void down(int root)
{
    int son1 = root << 1, son2 = root << 1 | 1;
    tree[son1].tag += tree[root].tag, tree[son2].tag += tree[root].tag;
    tree[son1].val += (tree[son1].r - tree[son1].l + 1) * tree[root].tag;
    tree[son2].val += (tree[son2].r - tree[son2].l + 1) * tree[root].tag;
    tree[root].tag = 0;
}

void update(int root, int l, int r, int add)
{
    if(tree[root].l >= l && tree[root].r <= r)
    {
        tree[root].tag += add;
        tree[root].val += (tree[root].r - tree[root].l + 1) * add;
        return;
    }
    if(tree[root].tag) down(root);
    int mid = (tree[root].l + tree[root].r) >> 1;
    if(l <= mid) update(root << 1, l, r, add);
    if(r > mid) update(root << 1 | 1, l, r, add);
    tree[root].val = tree[root << 1].val + tree[root << 1 | 1].val;
}

int ask(int root, int l, int r)
{
    if(tree[root].l >= l && tree[root].r <= r) return tree[root].val;
    if(tree[root].tag) down(root);
    int mid = (tree[root].l + tree[root].r) >> 1, ans = 0;
    if(l <= mid) ans += ask(root << 1, l, r);
    if(r > mid) ans += ask(root << 1 | 1, l, r);
    return ans;
}

int main()
{
    while(1)
    {
        char c = getchar();
        if(c == 'E') break;
        int num = read();
        a[++n].num = num;
        
        if(c == 'I') b[++cnt] = num, a[n].op = 1;
        else if(c == 'C') a[n].op = 2;
        else if(c == 'D') a[n].op = 3;
    }
    b[++cnt] = 0; //细节!为了求出比最差成绩高的成绩个数
    sort(b + 1, b + 1 + cnt);
    cnt = unique(b + 1, b + 1 + cnt) - b - 1;
    build(1, 1, cnt);
    for(int i = 1; i <= n; i++)
    {
        if(a[i].op == 1 && find(a[i].num) - 1 >= 1)
            update(1, 1, find(a[i].num) - 1, 1);
        else if(a[i].op == 3 && find(a[i].num) - 1 >= 1)
            update(1, 1, find(a[i].num) - 1, -1);
        else if(a[i].op == 2)
        {
            if(find(a[i].num) > cnt) printf("0\n");
            else printf("%d\n", ask(1, find(a[i].num), find(a[i].num)));
        }
    }
    return 0;
}

方格取数

Description

  • 为了使漫长的星际旅行变得更加有趣,航天员发明了一种益智游戏:一个由 NN个格子组成的棋盘上,填上 N N个标有数字的棋子,并给定一个整数 M,若相邻两个格子 (即有公共边的格子)中的棋子所标的数字之差的绝对值不超过 M,则可以将这两个棋子从棋 盘中拿去,游戏的目标是使剩下的棋子最少。 你的任务是写一个程序,对于一个初始棋盘和给定的 M 值,求出剩余棋子最少的取子方案。

Input

  • 输入文件 fgqs.in 的第一行给出 N 和 M 的值,这两个数之间用一个空格分开,其

    中 1≤N,M≤100。20%的数据 1≤N,M≤10。

    以下 N 行,每行有 N 个正整数,给出初始棋盘每个格子中棋子上所标的数,该数不超过

    1000,同一行相邻两个整数之间用一个空格分开。

Output

  • 输出文件 fgqs.out 仅有一个整数,即你得到的最佳取子方案中剩下的棋子数。

Sample Input

3 2

3 2 5

7 1 2

3 6 9

Sample Output

5

题解:

  • 思路1:搜索。

    • 考场上我就是这么做的。做的好的话20分。可是我爆掉了
  • 思路2:联通块

    • 第一次修正时是这样做的。即用搜索找出一个个互相间有关系的联通块。对于每个联通块,它所产生的贡献就是(int)(块里元素个数 / 2) * 2。最后ans就等于n * n - 所以块的贡献
    • 这样乍一看没有错误。但其实有个长这样的联通块:

    0

    0

    0

    0 0

    0

    • 按理说这个块的贡献是3。但你手玩就会发现贡献是2
  • 思路3:二分图匹配

    • 看来我对二分图还是不够敏感啊,这是老师讲的正解
    • 只要看到“两个东西匹配”这种字眼或者有这种感觉的题目,一定要联想到二分图
    • 跟以往不同的是,不用把图分成两部分,每个点都尝试匹配。不把图分成两部分的话就需要要更新mat时互相的mat都要更新
    • 这题也告诉了我们二分图匹配不一定一定要把图分成两部分
    • 你可能会问:怎么证明它时一个二分图?
    • 证明如下:

    Noip 模拟练习3_第1张图片

#include 
#include 
#include 
#include 
#define maxn 105
using namespace std;

struct Mat {int x, y;} mat[maxn][maxn];
int n, m, tot;
int a[maxn][maxn];
int dx[5] = {0, -1, 1, 0, 0};
int dy[5] = {0, 0, 0, -1, 1};
bool vis[maxn][maxn];

bool dfs(int x, int y)
{
    for(int i = 1; i <= 4; i++)
    {
        int xx = x + dx[i], yy = y + dy[i];
        if(xx >= 1 && xx <= n && yy >= 1 && yy <= n)
            if(abs(a[x][y] - a[xx][yy]) <= m && !vis[xx][yy])
            {
                vis[xx][yy] = 1;
                if(!mat[xx][yy].x || dfs(mat[xx][yy].x, mat[xx][yy].y))
                {
                    mat[xx][yy].x = x, mat[xx][yy].y = y;
                    mat[x][y].x = xx, mat[x][y].y = yy;
                    return 1;
                }
            }
    }
    return 0;
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            cin >> a[i][j];
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(!mat[i][j].x && dfs(i, j))
            {
                tot++;
                memset(vis, 0, sizeof(vis));
            }
    cout << n * n - 2 * tot;
    return 0;
}

你可能感兴趣的:(Noip 模拟练习3)