USACO 2021 January Contest 银级 题解

Problem 1. Dance Mooves

Description

Farmer John’s cows are showing off their new dance mooves!

At first, all NN cows ( 2 ≤ N ≤ 1 0 5 2≤N≤10^5 2N105) stand in a line with cow ii in the iith position in line. The sequence of dance mooves is given by KK ( 1 ≤ K ≤ 2 ∗ 105 1≤K≤2*105 1K2105) pairs of positions ( a 1 , b 1 ) , ( a 2 , b 2 ) , … , ( a K , b K ) (a_1,b_1),(a_2,b_2),…,(a_K,b_K) (a1,b1),(a2,b2),,(aK,bK). In each minute i = 1 … K i=1…K i=1K of the dance, the cows in positions a i a_i ai and b i b_i bi in line swap. The same K K K swaps happen again in minutes K + 1 … 2 K K+1…2K K+12K, again in minutes 2 K + 1 … 3 K 2K+1…3K 2K+13K, and so on, continuing indefinitely in a cyclic fashion. In other words,

  • In minute 1 1 1, the cows at positions a 1 a_1 a1 and b 1 b_1 b1 swap.
  • In minute 2 2 2, the cows at positions a 2 a_2 a2 and b2b2 swap.
  • In minute k k k, the cows in positions a k a_k ak and b k b_k bk swap.
  • In minute K + 1 K+1 K+1, the cows in positions a 1 a_1 a1 and b 1 b_1 b1 swap.
  • In minute K + 2 K+2 K+2, the cows in positions a 2 a_2 a2 and b 2 b_2 b2 swap.
  • and so on …

For each cow, please determine the number of unique positions in the line she will ever occupy.

Input

The first line contains integers N N N and K K K. Each of the next K K K lines contains ( a 1 , b 1 ) … ( a K , b K ) ( 1 ≤ a i < b i ≤ N ) (a1,b1)…(aK,bK)(1≤a_i<b_i≤N) (a1,b1)(aK,bK)(1ai<biN).

Output

Print N N N lines of output, where the iith line contains the number of unique positions that cow i i i reaches.

Sample Input
5 4
1 3
1 2
2 3
2 4
Sample Output
4
4
3
4
1
题目大意与分析

这题是说给定你一个数 N N N , 然后在 1 1 1 N N N 的位置上,每个位置上都有一头牛,初始编号为它们所在的位置。

然后给你一个 K K K, 是说有 K K K 次操作,每次选择两个位置上的牛进行交换

但是这 K K K 次操作可以无限循环下去,问你每头牛最后会出现在哪些位置上

很显然一直交换下去,必然会在某一次 K K K次操作的循环结束后回到起点

然后我们去统计每头牛出现的位置即可

但是这样做我们一开始并不知道要循环多少次就可以找到

如果一直循环下去,直到每头牛回到初始位置结束,时间复杂度太高

那么有没有什么规律呢

我们会发现在第一次 K K K 操作后会有某些数会构成一个循环

比如样例中 在第一次 K K K 操作后 1 − N 1-N 1N 位置上的牛会变成 2   4   3   1   5 2\ 4 \ 3 \ 1 \ 5 2 4 3 1 5

会发现有三个循环,分别是 4 − > 2 , 2 − > 1 , 1 − > 4 4->2,2->1,1->4 4>2,2>1,1>4 ; 3 − > 3 3->3 3>3; 5 − > 5 5->5 5>5

一看就会想到每个循环上的所有数经历一定的循环后一定会到这个循环上的所有位置,并且会遍历所有位置上可以到达的点

因此我们去找每个循环,然后记录当前循环中所有的可以到达的不同的点,然后把这个结果赋值给循环中的所有点即可

AC代码
#include<cstdio>
#include<vector>
#include<map> 
#include<algorithm>

using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const ll tmp = 1e6 + 10;
map<ll, bool> mpp;
vector<int> vc[N];
int sum[N], a[N], mp[N], b[N], c[N], mp2[N];

int main() {
	int n, m, u, v;
	scanf("%d%d", &n, &m);
	for(int i = 0; i <= n; i++) sum[i] = 1, a[i] = i, mpp[tmp * i + i] = 1, vc[i].push_back(i);
	for(int i = 0; i < m ;i++) {
		scanf("%d%d", &u, &v);
		if(!mpp[tmp * u+ a[v]]) {
			mpp[tmp * u + a[v]] = 1; sum[a[v]]++, vc[a[v]].push_back(u);
		}
		if(!mpp[tmp * v + a[u]]) {
			mpp[tmp * v + a[u]] = 1; sum[a[u]]++, vc[a[u]].push_back(v);
		}
		int t = a[u];
		a[u] = a[v];
		a[v] = t;
	}
	for(int i = 0; i <= n; i++) {
		b[a[i]] = i;
		mp[i] = 1;
	}
	for(int i = 1; i <= n; i++) {
		if(!mp[i]) continue;
		mp[i] = 0;
		int t = b[i];
		int maxx = sum[i];
		int s = 1;
		int ccnt = 0;
		for(int j = 0; j < vc[i].size(); j++) {
			c[ccnt++] = vc[i][j];
			mp2[vc[i][j]] = 1;
		}
		while(t != i) {
			mp[t] = 0;
			for(int j = 0; j < vc[t].size(); j++) {
				if(!mp2[vc[t][j]]) {
					c[ccnt++] = vc[t][j];
					mp2[vc[t][j]] = 1;
				}
			}
			t = b[t];
		}
		for(int i = 0; i < ccnt; i++) mp2[c[i]] = 0;
		t = b[i];
		sum[i] = ccnt;
		while(t != i) {
			sum[t] = ccnt;
			t = b[t];
		}
	}
	for(int i = 1; i <= n; i++) {
		printf("%d\n", sum[i]);
	}
	return 0;
}

Problem 2. No Time to Paint

Description

Bessie 最近收到了一套颜料,她想要给她的牧草地一端的栅栏上色。栅栏由 N N N 1 1 1 米长的小段组成( 1 ≤ N ≤ 1 0 5 1≤N≤10^5 1N105)。Bessie 可以使用 26 26 26 种不同的颜色,她将这些颜色由浅到深用字母 ‘A’ 到 ‘Z’ 标号(‘A’ 是很浅的颜色,‘Z’ 是很深的颜色)。从而她可以用一个长为 NN 且每个字符均为字母的字符串来描述她想要给栅栏的每一小段涂上的颜色。

初始时,所有栅栏小段均未被上色。Bessie 一笔可以给任意连续若干小段涂上同一种颜色,只要她不会在较深的颜色之上涂上较浅的颜色(她只能用较深的颜色覆盖较浅的颜色)。

例如,一段长为 4 的未被涂色的栅栏可以按如下方式上色:

.... -> BBB. -> BBLL -> BQQL

由于时间紧迫,Bessie 认为她可能需要放弃为栅栏上某个连续的区间上色!现在,她正在考虑 Q Q Q 个候选的区间( 1 ≤ Q ≤ 1 0 5 1≤Q≤10^5 1Q105),每个区间用满足 1 ≤ a ≤ b ≤ N 1≤a≤b≤N 1abN的两个整数 ( a , b ) (a,b) (a,b) 表示,为需要不上色的小段 a … b a…b ab的两端点位置。

对于每个候选区间,将所有区间外的栅栏小段都涂上所希望的颜色,并且区间内的栅栏小段均不涂色,最少需要涂多少笔?注意在这个过程中 Bessie 并没有真正进行任何的涂色,所以对于每个候选区间的回答是独立的。

Input

输入的第一行包含 N N N Q Q Q

下一行包含一个长为 Q Q Q 的字符串,表示每个栅栏小段所希望的颜色。

以下 Q Q Q 行,每行包含两个空格分隔的整数 A A A B B B,表示一个不涂色的候选区间。

Output

对于 Q Q Q 个候选区间的每一个,输出一行,包含答案。

Sample Input
8 2
ABBAABCB
3 6
1 4
Sample Output
4
3
Hint

在这个样例种,除去目标颜色 BAABBAAB 所对应的区间,涂上颜色需要四笔,而除去 ABBAABBA 仅需三笔。

.... -> AA.. -> ABBB -> ABCB
题目大意与分析

这题是说给你一个由 26 26 26 个大写字母组成的字符串, 26 26 26 个字母分别要用 26 26 26 种颜色的颜料染色,从 ‘A’ 到 ‘Z’,颜色逐渐由浅到深。并且深颜色的颜料可以覆盖浅颜色的。同时你可以一次对一个连续区间涂上相同的颜色。

问你将给定的字符串去掉一个区间之后其他的都涂成对应的颜色,最少需要涂多少次。

这题我一开始的想法是想先去遍历一遍当前的字符串,然后去遍历当前节点的后面的节点,看看是否由有和当前节点颜色一样的节点,有的话就标记后面的节点可以由当前节点染色,遇到一个比当前节点字母小的就结束。

for(int i = 1; i < n; i++) {
    if(mp[i]) continue;
    for(int j = i + 1; j <= n; j++) {
        if(s[j] < s[i]) break;
        if(s[j] == s[i]) {
            mp[j] = i;
        }
    }
}

然后去记录一个前缀和,去记录从第一个节点开始,到当前节点最少需要染多少次

for(int i = 1; i <= n; i++) {
    if(!mp[i]) sum[i] = sum[i - 1] + 1;
    else sum[i] = sum[i - 1];
}

然后对于每一次询问,给定一个区间 a , b a, b a,b ,需要的最少染色次数不就等于 sum[n] - sum[b] + sum[a - 1] 吗, 但是这个式子恒成立的条件只有当 b = = n b==n b==n 时才可以,因为去掉了区间 (a,b), 然后在区间(b + 1, n) 中有可能有从前面的点直接染色过来的,因此我们还需要去找有多少个点是从前面的点染色过来的,然后 a n s + + ans++ ans++ 即可

AC代码
#include<cstdio>
#include<cstring>

using namespace std;
const int N = 1e5 + 10;
char s[N];
int sum[N], mp[N], mp2[N];

int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	scanf("%s", s + 1);
	for(int i = 1; i < n; i++) {
		if(mp[i]) continue;
		for(int j = i + 1; j <= n; j++) {
			if(s[j] < s[i]) break;
			if(s[j] == s[i]) {
				mp[j] = i;
			}
		}
	} 
	for(int i = 1; i <= n; i++) {
		if(!mp[i]) sum[i] = sum[i - 1] + 1;
		else sum[i] = sum[i - 1];
	}
	int a, b;
	while(k--) {
		scanf("%d%d", &a, &b);
		if(b == n) printf("%d\n", sum[a - 1]);
		else {
			int ans = sum[n] - sum[b] + sum[a - 1];
			int minn = 0x3f3f3f3f;
			for(int i = b + 1; i <= n; i++) {
				if(mp[i] && mp[i] <= b && mp[i] < minn) {
					minn = mp[i];
					ans++;
					if(s[i] == 'A') break;
				}
			} 
			printf("%d\n", ans);
		}
	}
	return 0;
}
更优的解法

其实我们既然记录从第一个点到 a − 1 a - 1 a1 的需要最少的染色方案,如果只是求这个,我们在 O(1) 的时间内就可以求出来,那么为什么不倒过来去求 (n, b + 1)的需要的最少染色方案呢,这样是不是就可以保证在询问的时候的时间复杂度都变为 O(1) 了。

AC代码
#include<cstdio>
#include<cstring>

using namespace std;
const int N = 1e5 + 10;
char s[N];
int sum[N], mp[N], mp2[N], ss[N];

int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	scanf("%s", s + 1);
	for(int i = 1; i < n; i++) {
		if(mp[i]) continue;
		for(int j = i + 1; j <= n; j++) {
			if(s[j] < s[i]) break;
			if(s[j] == s[i]) {
				mp[j] = i;
			}
		}
	} 
	for(int i = 1; i <= n; i++) {
		if(!mp[i]) sum[i] = sum[i - 1] + 1;
		else sum[i] = sum[i - 1];
		mp[i] = 0; 
	}
	for(int i = n; i > 1; i--) {
		if(mp[i]) continue;
		for(int j = i - 1; j >= 1; j--) {
			if(s[j] < s[i]) break;
			if(s[j] == s[i]) {
				mp[j] = i;
			}
		}
	}
	for(int i = n; i >= 1; i--) {
		if(!mp[i]) ss[i] = ss[i + 1] + 1;
		else ss[i] = ss[i + 1]; 
	}
	int a, b;
	while(k--) {
		scanf("%d%d", &a, &b);
		printf("%d\n", sum[a - 1] + ss[b + 1]);
	}
	return 0;
}
对求从第一个点到当前点需要的最少染色数的优化

其实我们不用求当前点是由哪个点过来的,只要记录能不能由前面的点过来即可

AC代码
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
const int N = 1e5 + 10;
char s[N];
int sum[N], ss[N];
int minn[30];

int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	scanf("%s", s + 1);
	for(int i = 0; i < 26; i++) minn[i] = -1;
	for(int i = 1; i <= n; i++) {
		int curc = s[i] - 'A';
		for(int c = 0; c < 26; c++) minn[c] = min(curc, minn[c]);
		sum[i] = sum[i - 1];
		if(minn[curc] < curc) sum[i]++;
		minn[curc] = curc;
	}
	for(int i = 0; i < 26; i++) minn[i] = -1;
	for(int i = n; i >= 1; i--) {
		int curc = s[i] - 'A';
		for(int c = 0; c < 26; c++) minn[c] = min(curc, minn[c]);
		ss[i] = ss[i + 1];
		if(minn[curc] < curc) ss[i]++;
		minn[curc] = curc;
	}
	int a, b;
	while(k--) {
		scanf("%d%d", &a, &b);
		printf("%d\n", sum[a - 1] + ss[b + 1]);
	}
	return 0;
}

Problem 3. Spaced Out

Description

Farmer John 想要拍摄一张他的奶牛吃草的照片挂在墙上。草地可以用一个 N N N N N N 列正方形方格所组成的方阵表示(想象一个 N ∗ N N*N NN 的棋盘),其中 2 ≤ N ≤ 1000 2 \le N \le 1000 2N1000。在 Farmer John 最近拍摄的照片中,他的奶牛们太过集中于草地上的某个区域。这一次,他想要确保他的奶牛们分散在整个草地上。于是他坚持如下的规则:

  • 没有两头奶牛可以位于同一个方格。
  • 所有 2 ∗ 2 2*2 22的子矩阵(共有 ( N − 1 ) ∗ ( N − 1 ) (N - 1)*(N - 1) (N1)(N1)个)必须包含恰好 2 2 2 头奶牛。例如,这一放置方式是合法的:
CCC
...
CCC

而这一放置方式是不合法的,因为右下的 2 ∗ 2 2*2 22 正方形区域仅包含 1 1 1 头奶牛:

C.C
.C.
C..

没有其他限制。你可以假设 Farmer John 有无限多的奶牛(根据以往的经验,这种假设似乎是正确的……)。

Farmer John 更希望某些方格中包含奶牛。具体地说,他相信如果方格 ( i , j ) (i, j) (i,j) 中放有一头奶牛,照片的美丽度会增加 a i j a_{ij} aij 0 ≤ a i j ≤ 1000 0≤a_{ij}≤1000 0aij1000)单位。

求合法的奶牛放置方式的最大总美丽度。

Input

输入的第一行包含 N N N。以下 NN 行每行包含 N N N 个整数。从上到下第 i i i 行的第 j j j个整数为 a i j a_{ij} aij 的值。

Output

输出一个整数,为得到的照片的最大美丽度。

Sample Input
4
3 3 1 1
1 1 3 1
3 3 1 1
1 1 3 3
Sample Output
22
Hint

在这个样例中,最大美丽度可以在如下放置方式时达到:

CC..
..CC
CC..
..CC

这种放置方式的美丽度为 3 + 3 + 3 + 1 + 3 + 3 + 3 + 3 = 22 3+3+3+1+3+3+3+3=22 3+3+3+1+3+3+3+3=22

题目大意和分析

这题就是说给定一个 n ∗ n n * n nn 的矩阵,让你保证每个 2 ∗ 2 2 * 2 22 的矩阵都只选两个点并且必须选择两个点,不能多或者少,问你怎么样选择可以让最后的贡献最高

这题画几个样例会发现就是去找它的行奇偶行的最大值或者列的奇偶和的最大值

AC代码
#include<cstdio>
#include<algorithm>

using namespace std;
const int N = 1e3 + 10;
int a[N][N], n;

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			scanf("%d", &a[i][j]);
		}
	}
	int ans1 = 0, ans2 = 0;
	for(int i = 1; i <= n; i++) {
		int sum1 = 0, sum2 = 0;
		for(int j = 1; j <= n; j++) {
			if(j % 2) sum1 += a[i][j];
			else sum2 += a[i][j];
		}
		ans1 += max(sum1, sum2);
	} 
	for(int i = 1; i <= n; i++) {
		int sum1 = 0, sum2 = 0;
		for(int j = 1; j <= n; j++) {
			if(j % 2) sum1 += a[j][i];
			else sum2 += a[j][i];
		}
		ans2 += max(sum1, sum2);
	} 
	printf("%d\n", max(ans1, ans2));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值