zoukankan      html  css  js  c++  java
  • 9.11 Test——NOI2011 Day2

    T1:道路修建

    【问题描述】

    在 W 星球上有 n 个国家。为了各自国家的经济发展,他们决定在各个国家之间建设双向道路使得国家之间连通。但是每个国家的国王都很吝啬,他们只愿意修建恰好 n – 1 条双向道路。

    每条道路的修建都要付出一定的费用,这个费用等于道路长度乘以道路两端的国家个数之差的绝对值。例如,在下图中,虚线所示道路两端分别有 2 个、4 个国家,如果该道路长度为 1,则费用为 1×|2 – 4|=2。图中圆圈里的数字表示国家的编号。

    (图略)

    由于国家的数量十分庞大,道路的建造方案有很多种,同时每种方案的修建费用难以用人工计算,国王们决定找人设计一个软件,对于给定的建造方案,计算出所需要的费用。请你帮助国王们设计一个这样的软件。

    【输入格式】

    从文件 road.in 中读入数据。

    输入的第一行包含一个整数 n,表示 W 星球上的国家的数量,国家从 1 到 n编号。 接下来 n – 1 行描述道路建设情况,其中第 i 行包含三个整数 aibi ci,表示第 i 条双向道路修建在 ai bi 两个国家之间,长度为 ci

    【输出格式】

    输出到文件 road.out 中。

    输出一个整数,表示修建所有道路所需要的总费用。

    【样例输入】

    6

    1 2 1

    1 3 1

    1 4 2

    6 3 1

    5 2 1

    【样例输出】

    20

    【数据规模与约定】

    所有测试数据的范围和特点如下表所示

    测试点编号

    n 的规模(注意是等于号)

    约定

    1

    n = 2

    1≤ai, bin

    0 ≤ci≤ 106

    2

    n = 10

    3

    n = 100

    4

    n = 200

    5

    n = 500

    6

    n = 600

    7

    n = 800

    8

    n = 1000

    9

    n = 10,000

    10

    n = 20,000

    11

    n = 50,000

    12

    n = 60,000

    13

    n = 80,000

    14

    n = 100,000

    15

    n = 600,000

    16

    n = 700,000

    17

    n = 800,000

    18

    n = 900,000

    19

    n = 1,000,000

    20

    n = 1,000,000

    解析:

       开始时把题看错了以为要$LCT$,结果只需要把树建好,统计子树大小,每条边计算贡献即可,时间$O(n)$,(100分)

      $bzoj$上$dfs$好像过不了,需要把$dfs$改成$bfs$,然后就可过了,这里放$dfs$版的代码

     代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn = 1000004;
    
    inline int read()
    {
        int ret, f=1;
        char c;
        while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1;
        ret=c-'0';
        while((c=getchar())&&(c>='0'&&c<='9'))ret = (ret<<3)+(ret<<1)+c-'0';
        return ret*f;
    }
    
    int n, tot, head[maxn], siz[maxn];
    ll ans;
    struct edge{
        int nxt, to, d;
    }e[maxn<<1];
    
    void Addedge(int x, int y, int z)
    {
        e[++tot] = (edge){head[x], y, z};
        head[x] = tot;
    }
    
    void dfs(int x, int fa)
    {
        siz[x] = 1;
        for(int i = head[x]; i; i = e[i].nxt)
        {
            int id = e[i].to;
            if(id == fa)    continue;
            dfs(id, x);
            ans += ((siz[id]<<1) > n? (ll)((siz[id]<<1) - n) * (ll)e[i].d: (ll)(n - (siz[id]<<1)) * (ll)e[i].d);
            siz[x] += siz[id];    
        }
    }
    
    int main()
    {
        freopen("road.in", "r", stdin);
        freopen("road.out", "w", stdout);
        n = read();
        for(int i = 1; i < n; ++i)
        {
            int x = read(), y = read(), z = read();
            Addedge(x, y, z);
            Addedge(y, x, z);
        }
        dfs(1, 0);
        printf("%lld
    ", ans);
        return 0;
    }
    road

    T2:NOI嘉年华

    【问题描述】

    NOI2011 在吉林大学开始啦!为了迎接来自全国各地最优秀的信息学选手,吉林大学决定举办两场盛大的 NOI 嘉年华活动,分在两个不同的地点举办。每个嘉年华可能包含很多个活动,而每个活动只能在一个嘉年华中举办。

    现在嘉年华活动的组织者小安一共收到了 n 个活动的举办申请,其中第 i 个活动的起始时间为 Si,活动的持续时间为 Ti。这些活动都可以安排到任意一个嘉年华的会场,也可以不安排。 小安通过广泛的调查发现,如果某个时刻,两个嘉年华会场同时有活动在进行(不包括活动的开始瞬间和结束瞬间),那么有的选手就会纠结于到底去哪个会场,从而变得不开心。所以,为了避免这样不开心的事情发生,小安要求不能有两个活动在两个会场同时进行(同一会场内的活动可以任意进行)。

    另外,可以想象,如果某一个嘉年华会场的活动太少,那么这个嘉年华的吸引力就会不足,容易导致场面冷清。所以小安希望通过合理的安排,使得活动相对较少的嘉年华的活动数量最大。 此外,有一些活动非常有意义,小安希望能举办,他希望知道,如果第 i 个活动必须举办(可以安排在两场嘉年华中的任何一个),活动相对较少的嘉年华的活动数量的最大值。

    【输入格式】

    从文件 show.in 中读入数据。

    输入的第一行包含一个整数 n,表示申请的活动个数。

    接下来 n 行描述所有活动,其中第 i 行包含两个整数 SiTi,表示第 i 个活

    动从时刻 Si 开始,持续 Ti 的时间。

    【输出格式】

    输出到文件 show.out 中。

    输出的第一行包含一个整数,表示在没有任何限制的情况下,活动较少的嘉年华的活动数的最大值。 接下来 n 行每行一个整数,其中第 i 行的整数表示在必须选择第 i 个活动的前提下,活动较少的嘉年华的活动数的最大值。

    【评分标准】

    对于一个测试点:

    l   如果输出格式不正确(比如输出不足 n+1 行),得 0 分; l 如果输出文件第一行不正确,而且后 n 行至少有一行不正确,得 0 分;

    l   如果输出文件第一行正确,但后 n 行至少有一行不正确,得 4 分;

    l   如果输出文件第一行不正确,但后 n 行均正确,得 6 分;

    l   如果输出文件中的 n+1 行均正确,得 10 分。

    【样例输入】

    5

    8 2

    1 5

    5 3

    3 2

    5 3

     

    【样例输出】

    2

    2

    1

    2

    2

    2

    【样例说明】

    在没有任何限制的情况下,最优安排可以在一个嘉年华安排活动 1, 4,而在另一个嘉年华安排活动 3, 5,活动 2 不安排。

    【数据规模与约定】

    所有测试数据的范围和特点如下表所示

    测试点编号

    n 规模

    约定

    1

    1≤n≤10

    0≤Si≤109

    1≤Ti≤ 109

    2

    1≤n≤40

    3

    4

    1≤n≤200

    5

    6

    7

    8

    9

    10

    解析:

       考场时大概猜到是$DP$,但太菜了想不到怎么$DP$,于是就只写了$10$分的暴力,最后结果还只有$4$分

      先把时间离散化,设总时间为$cnt$

      处理出$H[i][j]$,表示活动的开始与结束时间都在$[i, j]$中的活动数, $O(n^{2})$

      再处理出$f[i][j]$, 表示前$i$的时间内,一个分会场安排$j$个活动,另一个分会场最多安排多少活动, 注意这个有两个转移方式, 设$k$为之前的某个时间点,显然$H[k][i]$可以安排在分会场一或分会场二, 因此我们有:

      $f[i][j] = max(f[i][j], f[k][j] + H[k][i])$, $f[i][j] = max(f[i][j], f[k][j-H[k][i]])$

      $O(n^{3})$

      继续处理出$g[i][j]$,表示从$i$开始到最后的时间内,分会场一安排$j$个活动,分会场二最多安排多少活动, 转移方式与$f$数组类似

      $O(n^{3})$

      显然可以得到没有任何限制情况下的答案:

      $ans = maxleft { min(i, f[cnt][i]) ight } (0 leqslant i leqslant n)$

      $O(n)$

      麻烦就在于如何处理有限制条件下的答案

      再设一个$dp[i][j]$, 表示时间$[i, j]$内的活动必选的情况下,活动最少的分会场最多能安排多少活动,容易得到一个简单的转移方程:

      $dp[i][j] = maxleft { min(l+H[i][j]+r, f[i][l]+g[j][r]) ight }$

      $l$表示在前$i$的时间内安排$l$个活动,$r$表示在后$j$的时间内安排$r$个活动, 并且这$l$与$r$个活动与时间$[i, j]$内的活动安排在同一会场

      这个转移是$O(n^{4})$的, 显然会$T$

      考虑到对于任一$l$,随着的$l$增加,$f[i][l]$必然减少,$r$与$g[j][r]$也是同理。那么对于同一个$[i, j]$,随着$l$的增加,$r$一定比上一次在$l-1$时取的$r$小,因此可以把$r$改成一个从右向左移动的指针, 这样时间复杂度就降成了$O(n^{3})$,就可过了

      最后对于一个时间是$[l,r]$的活动, 它的答案应该是$maxleft { dp[i][j] ight }(1leqslant ileqslant l, rleqslant jleqslant cnt)$, 因为取$dp[l][r]$并不一定是最优的情况, 通过$dp$数组的转移方程就可以看出可能存在$dp[i][j] geqslant dp[l][r] (1leqslant ileqslant l, rleqslant jleqslant cnt)$

      细节还是看代码吧

     代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn = 505, inf = 0x3f3f3f3f;
    
    int n, a[maxn], cnt;
    int H[maxn][maxn], f[maxn][maxn], dp[maxn][maxn], g[maxn][maxn];
    int ans[maxn];
    struct seg{
        int l, r, id;
    }s[maxn];
    
    bool cmp(seg x, seg y)
    {
        return x.r != y.r? x.r < y.r: x.l > y.l;
    }
    
    int main()
    {
        freopen("show.in", "r", stdin);
        freopen("show.out", "w", stdout);
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            s[i].l = x;
            s[i].r = x + y;
            s[i].id = i;
            a[++cnt] = x;
            a[++cnt] = x + y;
        }
        sort(a + 1, a + cnt + 1);
        cnt = unique(a + 1, a + cnt + 1) - a - 1;
        sort(s + 1, s + n + 1, cmp);
        for(int i = 1; i <= n; ++i)
        {
            s[i].l = lower_bound(a + 1, a + cnt + 1, s[i].l) - a;
            s[i].r = lower_bound(a + 1, a + cnt + 1, s[i].r) - a;
        }
        for(int i = 1; i <= cnt; ++i)
            for(int j = 0; j <= n; ++j)
                f[i][j] = g[i][j] = -inf;
        int now = 1;    
        for(int i = 1; i <= cnt; ++i)
            for(int j = i - 1; j >= 1; --j)
            {
                H[j][i] = H[j][i - 1] + H[j+1][i] - H[j+1][i-1];
                while(s[now].r == i && s[now].l == j)
                {
                    H[j][i] ++;
                    now ++;
                }    
            }
        for(int i = 1; i <= cnt; ++i)
            for(int j = 0; j <= H[1][i]; ++j)
                if(j == 0)
                    f[i][j] = H[1][i];
                else
                    for(int k = 1; k < i; ++k)
                    {
                        if(j <= H[1][k])
                            f[i][j] = max(f[i][j], f[k][j] + H[k][i]);
                        if(j >= H[k][i])
                            f[i][j] = max(f[i][j], f[k][j-H[k][i]]);
                    }
        for(int i = cnt; i >= 1; --i)
            for(int j = 0; j <= H[i][cnt]; ++j)
                if(j == 0)
                    g[i][j] = H[i][cnt];
                else
                    for(int k = i + 1; k <= cnt; ++k)
                    {
                        if(j <= H[k][cnt])    
                            g[i][j] = max(g[i][j], g[k][j] + H[i][k]);
                        if(j >= H[k][i])
                            g[i][j] = max(g[i][j], g[k][j-H[i][k]]); 
                    }
        for(int i = 0; i <= n; ++i)
            ans[0] = max(ans[0], min(i, f[cnt][i]));
        for(int i = 1; i <= cnt; ++i)
            for(int j = i + 1; j <= cnt; ++j)
            {
                int l = 0, r = n, now, nxt;
                for(; l <= n; ++l)
                {
                    for(; r; --r)
                    {
                        now = min(l + r + H[i][j], f[i][l] + g[j][r]);
                        nxt = min(l + r - 1 + H[i][j], f[i][l] + g[j][r-1]);
                        if(nxt < now)
                            break;
                    }    
                    if(!r)
                        now = min(l + r + H[i][j], f[i][l] + g[j][r]);
                    dp[i][j] = max(now, dp[i][j]);
                }    
            }
        for(int i = 1; i <= n; ++i)
        {
            for(int l = 1; l <= s[i].l; ++l)
                for(int r = s[i].r; r <= cnt; ++r)
                    ans[s[i].id] = max(ans[s[i].id], dp[l][r]);
        }
        for(int i = 0; i <= n; ++i)
            printf("%d
    ", ans[i]);
        return 0;
    }
    show

    T3:兔兔与蛋蛋游戏

    【问题描述】

    这些天,兔兔和蛋蛋喜欢上了一种新的棋类游戏。 这个游戏是在一个 n m 列的棋盘上进行的。游戏开始之前,棋盘上有一个格子是空的,其它的格子中都放置了一枚棋子,棋子或者是黑色,或者是白色。

    每一局游戏总是兔兔先操作,之后双方轮流操作,具体操作为:

    l 兔兔每次操作时,选择一枚与空格相邻的白色棋子,将它移进空格。 l 蛋蛋每次操作时,选择一枚与空格相邻的黑色棋子,将它移进空格。

    第一个不能按照规则操作的人输掉游戏。为了描述方便,下面将操作“将第x 行第 y 列中的棋子移进空格中”记为 M(x,y)。

    例如下面是三个游戏的例子。

    (略)

    最近兔兔总是输掉游戏,而且蛋蛋格外嚣张,于是兔兔想请她的好朋友——你——来帮助她。她带来了一局输给蛋蛋的游戏的实录,请你指出这一局游戏中所有她“犯错误”的地方。

    注意:

    l   两个格子相邻当且仅当它们有一条公共边。

    l   兔兔的操作是“犯错误”的,当且仅当,在这次操作前兔兔有必胜策略,而这次操作后蛋蛋有必胜策略。

    【输入格式】

    从文件 game.in 中读入数据。 输入的第一行包含两个正整数 nm

    接下来 n 行描述初始棋盘。其中第 i 行包含 m 个字符,每个字符都是大写英文字母"X"、大写英文字母"O"或点号"."之一,分别表示对应的棋盘格中有黑色棋子、有白色棋子和没有棋子。其中点号"."恰好出现一次。

    接下来一行包含一个整数 k(1≤k≤1000),表示兔兔和蛋蛋各进行了 k 次操作。

    接下来 2k 行描述一局游戏的过程。其中第 2i – 1 行是兔兔的第 i 次操作(编号为 i 的操作),第 2i 行是蛋蛋的第 i 次操作。每个操作使用两个整数 x,y 来描述,表示将第 x 行第 y 列中的棋子移进空格中。 输入保证整个棋盘中只有一个格子没有棋子,游戏过程中兔兔和蛋蛋的每个操作都是合法的,且最后蛋蛋获胜。

    【输出格式】

    输出到文件 game.out 中。

    输出文件的第一行包含一个整数 r,表示兔兔犯错误的总次数。

    接下来 r 行按递增的顺序给出兔兔“犯错误”的操作编号。其中第 i 行包含一个整数 ai 表示兔兔第 i 个犯错误的操作是他在游戏中的第 ai 次操作。

    【输入样例 1】

    1 6

    XO.OXO

    1

    1 2

    1 1

     

    【输出样例 1】

    1

    1

     

    【输入样例 2】

    3 3

    XOX

    O.O XOX

    4

    2 3

    1 3

    1 2

    1  1

    2  1

    3  1

    3 2

    3  3

    【输出样例 2】

    0

     

    【输入样例 3】

    4  4

    OOXX

    OXXO

    OO.O

    XXXO

    2

    3 2

    2 2

    1 2

    1 3

    【输出样例 3】

    2

    1

    2

    【样例说明】

    样例 1 对应图一中的游戏过程。 样例 2 对应图三中的游戏过程。

     

    【数据规模】

    所有测试数据的范围和特点如下表所示

    测试点编号

    n 的规模

    m 的规模

    1

    n = 1

    1≤ m≤ 20

    2

    3

    n = 3

    m = 4

    4

    n = 4

    m = 4

    5

    6

    n = 4

    m = 5

    7

     8

    n = 3

    m = 7

    n = 2

    1 ≤m≤ 40

     9
    10
    11
    12

    13

    14

    15

    1 ≤n≤ 16

    1 ≤m≤ 16

    16

    17

    1 ≤n≤ 40

    1 ≤m≤ 40

    18

    19

    20

     解析:

       博弈论转化为二分图

      首先是有一个性质,移动棋子的路径不会交叉

      把 . 看做X, 然后把X当做一边的点, 再把O看做另一边的点, 每个原图中的点向四周不同颜色的点连边
      然后又有一个性质,如果存在一个最大匹配不包含当前移动的点,当前移动的人必败, 否则必胜
      因此,我们可以判定兔兔操作之前,即蛋蛋上一次操作之后,兔兔是否是必胜态,也就是蛋蛋是否是必败态,再判定兔兔操作后是否是必败态,如果兔兔操作之前是必胜态,操作后变成了必败态,这就是犯错误的地方
     代码:
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn = 50;
    
    int n, m, sx, sy;
    char s[maxn][maxn];
    
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    
    int head[maxn*maxn], tot;
    struct edge{
        int nxt, to;
    }e[maxn*maxn*4];
    
    void Addedge(int x, int y)
    {
        e[++tot] = (edge){head[x], y};
        head[x] = tot;
    }
    
    int mat[maxn*maxn], ban[maxn*maxn], vis[maxn*maxn], timer;
    int ans[maxn*maxn], cnt;
    bool flag[maxn*maxn];
    
    bool dfs(int x)
    {
        if(vis[x] == timer)    return 0;
        vis[x] = timer;
        for(int i = head[x]; i; i = e[i].nxt)
        {
            int id = e[i].to;
            if(ban[id] == 1)    continue;
            if(!mat[id] || dfs(mat[id]))
            {
                mat[id] = x;
                mat[x] = id;
                return 1;
            }
        }
        return 0;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i)
            scanf("%s", s[i] + 1);
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j)
            {
                if(s[i][j] == '.')
                {
                    sx = i;
                    sy = j;
                }
                if(s[i][j] == 'O')
                    for(int k = 0; k < 4; ++k)
                        if(i + dx[k] >= 1 && i + dx[k] <= n && j + dy[k] >= 1 && j + dy[k] <= m && s[i][j] != s[i+dx[k]][j+dy[k]])
                        {
                            Addedge((i - 1) * m + j, (i + dx[k] - 1) * m + j + dy[k]);
                            Addedge((i + dx[k] - 1) * m + j + dy[k], (i - 1) * m + j);
                        }
            }
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j)
                if(!mat[(i - 1) * m + j])
                {
                    ++timer;
                    dfs((i - 1) * m + j);
                }
        int q;
        scanf("%d", &q);
        for(int i = 1; i <= (q<<1); ++i)
        {
            int x = (sx - 1) * m + sy;
            ban[x] = 1;
            if(mat[x])
            {
                int y = mat[x];
                mat[x] = mat[y] = 0;
                ++timer;
                flag[i] = !dfs(y);
            }
            scanf("%d%d", &sx, &sy);    
            if(!(i&1))
                if(flag[i-1] && flag[i])
                    ans[++cnt] = (i>>1);
        }
        printf("%d
    ", cnt);
        for(int i = 1; i <= cnt; ++i)
            printf("%d
    ", ans[i]);
        return 0;
    }
    game
  • 相关阅读:
    Linux 安装 PostgreSQL
    【Linux】ZeroMQ 在 centos下的安装
    Celery
    Django学习之完成数据库主从复制、读写分离和一主多从情况下的使用办法
    python异步编程之asyncio(百万并发)
    Python正则表达式中的re.S,re.M,re.I的作用
    云开发 :云原生(Cloud Native)
    极简Unity调用Android方法
    UnityShader快速上手指南(四)
    UnityShader快速上手指南(三)
  • 原文地址:https://www.cnblogs.com/Joker-Yza/p/11515082.html
Copyright © 2011-2022 走看看