USACO2018 OPEN TEST - Silver
1. Lemonade Line
题目描述
这是农场上一个炎热的夏日,Farmer John要给他的 NNN 头奶牛发柠檬汽水了!所有的 NNN 头奶牛(方便起见,编号为 1…N1 \dots N1…N)都喜欢柠檬汽水,只是有些喜欢的程度更高一些。具体地说,奶牛 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=7 和 w=400w = 400w=400 的奶牛先到并等在队伍中。然后 w=1w = 1w=1 的奶牛到达并且会离开,这是由于已经有 222 头奶牛在队伍中了。然后 w=2w = 2w=2 的两头奶牛到达,一头留下排队,一头离开。
对于 100%100\%100% 的数据,1≤N≤1051 \le N \le 10^51≤N≤105,0≤wi≤1090 \le w_i \le 10^90≤wi≤109。
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)(1≤N≤100,000)。接下来 NNN 行描述了 A[0]…A[N−1]A[0] \ldots A[N-1]A[0]…A[N−1],每个数都是一个范围为 0…1090 \ldots 10^90…109 的整数。输入数据不保证各不相同。
输出格式
输出 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 组成的。如果编号为 111 和 999 的奶牛组队,她们可以形成一个大小为 101010 的领域。
对于 100%100\%100% 的数据,1≤N≤2501 \le N \le 2501≤N≤250;对于每一个 ccc,0≤c≤1060 \le c \le 10^60≤c≤106。
题解
-
T1T1T1 真的很水,排序后扫一遍就可以过了,代码这里并不提供。
-
T2T2T2 可能要稍加思考。如果我们直接按照题目中给我们的代码做的话,只能过 555 个点。但是题目中执行的是冒泡排序,每一次是交换两个数。那我们只要求出一个数在排序前后的两个序列中最大的位置差即可。
-
T3T3T3 是真的要好好思考一番。
前一个答案我们用 BFSBFSBFS 很快就可以得出答案,但是第二个答案就没有那么容易了。这里提供一种新的方法:并查集。我们可以将颜色相同的两个点之间连一条边,然后 ans1ans1ans1 就是求度最大的数。对于 ans2ans2ans2,我们可以每次将两棵树之间连一条边,然后找到当前的最大的树,再不断回溯,直至找到最大的答案。
但是这题有几个注意点
1.1.1. 为了方便我们做题,我们默认将一个点和它右边下面的点连一条边。但是要注意边界问题。
2.2.2. 此题码量可能有点大,要注意写题时一定要细心。
【我检查一个等于号就检查了一早上……】 -
各题代码(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); }