USACO2018 OPEN TEST - Silver

本文解析了USACO2018公开赛Silver组的三道题目,包括LemonadeLine,OutofSorts[S.]和MultiplayerMoo。详细介绍了每道题目的背景、算法思路及解决方案,涵盖了排序、冒泡排序优化和并查集算法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

USACO2018 OPEN TEST - Silver

1. Lemonade Line

题目描述

这是农场上一个炎热的夏日,Farmer John要给他的 NNN 头奶牛发柠檬汽水了!所有的 NNN 头奶牛(方便起见,编号为 1…N1 \dots N1N)都喜欢柠檬汽水,只是有些喜欢的程度更高一些。具体地说,奶牛 iii 为了获得柠檬汽水最多愿意排在wiw_iwi 头奶牛之后。现在所有的 NNN 头奶牛都在田里,但是只要Farmer John敲响牛铃,这些奶牛就会立刻赶到Farmer John的柠檬汽水站。她们会在Former John开始分发柠檬汽水之前到达,但是没有两头奶牛会在同一时刻到达。此外,当奶牛 iii 到达时,当且仅当至多有 wiw_iwi 头奶牛在排队时她会来排队。

Farmer John想要提前准备一定量的柠檬汽水,但是他不想浪费。排队的奶牛的数量可能取决于她们到达的顺序。帮助他求出最少可能的排队的奶牛数量。

输入格式

第一行包含 NNN,第二行包含 NNN 个用空格分隔的整数 w1,w2,…,wNw_1, w_2, \dots, w_Nw1,w2,,wN

输出格式

共一行,输出在所有可能的奶牛到达顺序之下,最小可能的排队的奶牛数量。

样例输入
5
7 1 400 2 2
样例输出
3
数据范围与提示

在这个情况下,可能最后仅有三头奶牛在队伍中(这也是最小可能值)。假设 w=7w = 7w=7w=400w = 400w=400 的奶牛先到并等在队伍中。然后 w=1w = 1w=1 的奶牛到达并且会离开,这是由于已经有 222 头奶牛在队伍中了。然后 w=2w = 2w=2 的两头奶牛到达,一头留下排队,一头离开。

对于 100%100\%100% 的数据,1≤N≤1051 \le N \le 10^51N1050≤wi≤1090 \le w_i \le 10^90wi109

2. Out of Sorts [S.]

题目描述

留意着农场之外的长期职业生涯的可能性,奶牛Bessie开始在不同的在线编程网站上学习算法。

她到目前为止最喜欢的算法是“冒泡排序”。这是Bessie的对长度为 NNN 的数组 AAA 进行排序的奶牛码实现。

sorted = false
while (not sorted):
   sorted = true
   moo
   for i = 0 to N-2:
      if A[i+1] < A[i]:
         swap A[i], A[i+1]
         sorted = false

显然,奶牛码中的 moo 指令的作用只是输出 moo 。奇怪的是,Bessie看上去执着于在她的代码中的不同位置使用这个语句。

给定一个输入数组,请预测Bessie的代码会输出多少次 moo

输入格式

输入的第一行包含 NNN (1≤N≤100,000)(1 \leq N \leq 100,000)(1N100,000)。接下来 NNN 行描述了 A[0]…A[N−1]A[0] \ldots A[N-1]A[0]A[N1],每个数都是一个范围为 0…1090 \ldots 10^90109 的整数。输入数据不保证各不相同。

输出格式

输出 moo 被输出的次数。

样例输入
5
1
5
3
8
2
样例输出
4
数据范围与提示

3. Multiplayer Moo

题目描述

奶牛们提出了一款创新性的新游戏,惊讶的是她们给这款游戏取了个最没创意的名字:“Moo”。 Moo游戏在一个由 N×NN \times NN×N 个正方形格子组成的棋盘上进行,一头奶牛可以通过大叫一声“哞!”然后把她的数字编号写在这个格子里来占有这个格子。

在游戏结束的时候,每个格子中都包含一个数。在这个时刻,如果一头奶牛创建了一个由连通的格子组成的领域,大小不小于其他所有领域,那这头奶牛就获胜。一个”领域”被定义为一些具有相同数字编号的格子,其中每个在领域中的格子都直接与另一个同一领域中的格子通过上、下、左或者是右相邻(对角线不计)。

由于以单牛形式进行游戏有点无聊,奶牛们也对双牛组队进行游戏感兴趣。同一队的两头奶牛像之前一样可以创建一个领域,但是现在领域中的格子可以属于队伍中的任一头奶牛。

给定游戏棋盘的最终状态,请帮助奶牛们计算任何单头奶牛拥有的最大的领域包含的格子数量,以及任何两头奶牛组成的队伍占有的最大的领域包含的格子的数量。两头奶牛占有的领域必须要同时包含队伍中两头奶牛的编号,不能仅仅包含一头。

输入格式

输入的第一行包含 NNN。下面 NNN 行,每行包含 NNN 个整数 ccc,描述棋盘的最终状态。棋盘中至少出现两种不同的数字。

输出格式

输出的第一行描述任何单头奶牛占有的最大领域大小,第二行描述任何两头奶牛的队伍占有的最大领域的大小。

样例输入
4
2 3 9 3
4 9 9 1
9 9 1 7
2 1 1 9
样例输出
5
10
数据范围与提示

在这个例子中,单头奶牛占有的最大领域是由五个 999 组成的。如果编号为 111999 的奶牛组队,她们可以形成一个大小为 101010 的领域。

对于 100%100\%100% 的数据,1≤N≤2501 \le N \le 2501N250;对于每一个 ccc0≤c≤1060 \le c \le 10^60c106

题解

  1. T1T1T1 真的很水,排序后扫一遍就可以过了,代码这里并不提供。

  2. T2T2T2 可能要稍加思考。如果我们直接按照题目中给我们的代码做的话,只能过 555 个点。但是题目中执行的是冒泡排序,每一次是交换两个数。那我们只要求出一个数在排序前后的两个序列中最大的位置差即可。

  3. T3T3T3 是真的要好好思考一番。

    前一个答案我们用 BFSBFSBFS 很快就可以得出答案,但是第二个答案就没有那么容易了。这里提供一种新的方法:并查集。我们可以将颜色相同的两个点之间连一条边,然后 ans1ans1ans1 就是求度最大的数。对于 ans2ans2ans2,我们可以每次将两棵树之间连一条边,然后找到当前的最大的树,再不断回溯,直至找到最大的答案。

    但是这题有几个注意点

    1.1.1. 为了方便我们做题,我们默认将一个点和它右边下面的点连一条边。但是要注意边界问题。

    2.2.2. 此题码量可能有点大,要注意写题时一定要细心。【我检查一个等于号就检查了一早上……】

  4. 各题代码(T1T1T1 过水,代码就不放了)

    //T2:Out of Sorts [S.]
    #include <bits/stdc++.h>
    #define int long long
    #define M 100100
    using namespace std;
    
    int n, ans=-1;
    struct Node{
        int val, t;
    }a[M];
    
    inline int cmp(Node x,Node y)
    {
        if(x.val!=y.val) return x.val<y.val;
        else return x.t<y.t;
    }
    
    inline int read()
    {
        int re=0, f=1; char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0' && ch<='9') {re=re*10+(ch-'0'); ch=getchar();}
        return re*f;
    }
    
    signed main()
    {
        n=read();
        for(int i=1;i<=n;++i) a[i].val=read(), a[i].t=i;
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;++i) ans=max(ans,a[i].t-i);
        printf("%lld\n",ans+1);
        return 0;   
    }
    
    //T3:Multiplayer Moo
    #include <bits/stdc++.h>
    #define M 1001000
    #define Inf 0x3f3f3f3f
    using namespace std;
    
    struct Node{
        int from, to, co1, co2;
    }c[M];
    int n, m, ans1=0, ans2=0, tot=0;
    int num[M], fa[M], size[M], a[300][300];
    vector<int> v[M];
    
    inline int read()
    {
        int re=0, f=1; char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0' && ch<='9') {re=re*10+(ch-'0'); ch=getchar();}
        return re*f;
    }
    
    inline int find(int x)
    {
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    
    inline void Prepare()
    {
        for(int i=1;i<=n*n;++i) fa[i]=i, size[i]=1;
    }
    
    inline void Merge(int x,int y)
    {
        int f1=find(x), f2=find(y);
        if(f1==f2) return ;
        if(size[f2]>size[f1]) swap(f2,f1);
        fa[f2]=f1;
        size[f1]+=size[f2];
    }
    
    inline void Add_edge(int num1,int num2,int x,int y,int flag)
    {
        tot++;
        c[tot].from=find(num1), c[tot].to=find(num2);
        if(flag==1)
        {
            c[tot].co1=min(a[x][y],a[x+1][y]);
            c[tot].co2=max(a[x][y],a[x+1][y]);
        }
        else 
        {
            c[tot].co1=min(a[x][y],a[x][y+1]);
            c[tot].co2=max(a[x][y],a[x][y+1]);
        }
    }
    
    bool cmp(Node x,Node y)
    {
        if(x.co1==y.co1) return x.co2<y.co2;
        return x.co1<y.co1;
    }
    
    inline void Clear(int x)
    {
        for(int i=0;i<v[x].size();++i)
        {
            int u=v[x][i];
            fa[u]=u;
            size[u]=num[u];
        }
    }
    
    int main()
    {
        n=read();
        for(int i=1;i<=n;++i)
          for(int j=1;j<=n;++j)
            a[i][j]=read();
        Prepare();
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(i!=n && a[i][j]==a[i+1][j]) Merge((i-1)*n+j,i*n+j);
                if(j!=n && a[i][j]==a[i][j+1]) Merge((i-1)*n+j,(i-1)*n+j+1);
                ans1=max(ans1,size[find((i-1)*n+j)]);
            }
        }
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                int x=(i-1)*n+j;
                if(find(x)==x) v[a[i][j]].push_back(x);
            }
        }
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(i!=n && a[i][j]!=a[i+1][j]) Add_edge((i-1)*n+j, i*n+j, i, j, 1);
                if(j!=n && a[i][j]!=a[i][j+1]) Add_edge((i-1)*n+j, (i-1)*n+j+1, i, j, 2);
            }
        }
        sort(c+1,c+tot+1,cmp);
        for(int i=1;i<=n*n;++i) num[i]=size[i];
        for(int i=1;i<=tot;)
        {
            int color1=c[i].co1, color2=c[i].co2;
            while(c[i].co1==color1 && c[i].co2==color2)
            {
                Merge(c[i].from,c[i].to);
                ans2=max(ans2,size[find(c[i].from)]);
                i++;
            }
            Clear(color1), Clear(color2);
        }
        printf("%d\n%d\n",ans1,ans2);
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值