[考试记录] 2024.6.9
T1 奶牛翻转(cowtip)
Farmer John 有时会苦恼于一些无聊的少年在晚上到访他的农场并把一些奶牛翻转过来。他在某天早上醒来时发现这件事情再次发生了一一在前一夜他的 \(N^2\) 头奶牛还在 \( 𝑁 × 𝑁 ( 1 ≤ 𝑁 ≤ 10 ) N×N(1≤N≤10)\) 的网格状的草地上吃草, 而到了早上其中一些奶牛却被翻转过来了。
幸运的是, Farmer John 用拖拉机和叉车上的零件制作了一台很棒的机器, 奶牛翻转器3000, 这台机器可以帮他尽快地将这些奶牛翻转回来。他可以在网格左上角的任意一块矩形草地上(包含左上角奶牛的一个矩形网格)使用这台机器。当他这么做时, 机器将把该矩形中的所有奶牛全部翻转一次, 让原来四脚朝天的奶牛重新站好, 但不幸的是, 机器也同时会将原来站好的奶牛翻转过来!换句话说, 机器可以来回改变矩形网格中每头奶牛的状态。
Farmer John 认为, 如果合适地使用足够多次机器, 他可以把所有奶牛的状态都恢复成站好的状态。请帮他求出最少需要使用几次机器来达到他的目标。
注意到, 在同一块矩形草地上使用两次机器是毫无意义的, 因为这不会对矩形中的任何奶牛产生任何影响, 因此应该考虑对于每个左上角矩形最多使用一次机器。
输入的第一行包含整数 \( 𝑁\)。接下来 \(N\) 行, 每行给出一个长度为 \(N\) 的字符串, \(0\) 表示站好的奶牛, 而 \(1\) 表示四角朝天的奶牛。
输出 Farmer John 为了把所有奶牛的状态都恢复成站好的状态所使用奶牛翻转器 3000 的最少次数。
解析
对于矩阵最右下角 \((n,n)\) 的奶牛必须用范围最大的矩阵来反转,这样就得出了最大矩阵的反转次数。接着是 \((n,n-1)\) 和 \((n-1,n)\) 的奶牛,同理得出他们所需反转的次数。反转过程模拟即可。
#include<bits/stdc++.h>
using namespace std;
int n;
bitset<11> G[11];
long long ans;
char k;
int main(){
freopen("cowtip.in", "r", stdin);
freopen("cowtip.out", "w", stdout);
ios::sync_with_stdio(0) ,cin.tie(0), cout.tie(0);
cin>>n;
for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j) cin>>k, G[i][j] = k=='1'?1:0;
for(int i=n; i>=1; --i) for(int j=n; j>=1; --j){
if(!G[i][j]) continue;
++ans;
for(int x=i; x>=1; --x) for(int y=j; y>=1; --y) G[x].flip(y);
} return cout<<ans, 0;
}
T2 格格不入 (outofplace)
满怀雄心壮志的 Farmer John 计划尝试做一些似平永远不太对劲的事情:他想给他的整个牛群拍照。 为了使照片好看, 他希望奶牛们按照从矮到高的顺序排成一排。不幸的是, 就在他让奶牛们以这种方式排好之后,奶牛 Bessie(一直是麻烦制造者)走出队伍, 并将自己插入到了队伍中的其他位置!
Farmer John 想要交换许多对奶牛, 使得整个牛群重新排好队。请你帮他求出, 最少需要交换多少对奶牛, 才能实现他的目标。
输入的一行包括一个整数 $
𝑁
(
2
≤
𝑁
≤
100
)
N(2≤N≤100) $。接下来 \(N\) 行给出了 Bessie 在移动之后队伍中的每头奶牛的身高。每头奶牛身高在 \([1,10^6]\) 中, 一些奶牛的身高可能相同。
解析
容易知道,一个序列的逆序对数量就是这个序列所需交换的左小次数。但这道题需要去重,不能有一个连续子序列都为相等的数。比如 \(1,1,2,1,1,3\)
去成 \(1,2,1,3\) 即可。归并求逆序对即可。
#include<bits/stdc++.h>
using namespace std;
int n, m[101], ans, cnt;
unordered_map<int, bool> mp;
inline void mergesort(int l, int r, int mid){
int i = l, j = mid+1, k = 0, b[101];
while(i <= mid && j <= r){
if(m[i] > m[j]) b[k++] = m[j++], ans += mid-i+1;
else b[k++] = m[i++];
}
while(i <= mid) b[k++] = m[i++];
while(j <= r) b[k++] = m[j++];
k = 0;
for(int i=l; i<=r; ++i) m[i] = b[k++];
}
inline void merge(int l, int r){
if(l >= r) return;
int mid = (l+r) >> 1;
merge(l, mid), merge(mid+1, r);
mergesort(l, r, mid);
}
int main(){
freopen("outofplace.in", "r", stdin);
freopen("outofplace.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n;
for(int i=1; i<=n; ++i){
cin>>m[++cnt];
if(m[cnt] != m[cnt-1]) mp[m[cnt-1]] = 0;
if(mp[m[cnt]]) --cnt;
else mp[m[cnt]] = 1;
}
merge(1, cnt);
return cout<<ans, 0;
}
T3 WRONG - Wrong directions
题面翻译
Farmer John刚刚购买了一台新型可编程拖拉机。为了使拖拉机移动,他输入一串长度为N的绳子
(1 <= N <= 100,000)仅由字符F,L和R组成。每个'F'指示拖拉机向前移动一个单元,
并且字符'L'和'R'分别导致左右转90度。拖拉机从原点开始(0,0)
朝北。
通过输入他想要的命令字符串对他的拖拉机进行编程后,FJ记得他输入了一个字符
命令字符串不正确,但他不记得是哪一个!例如,他可能在打算输入'F'或'L'时
string包含字符'R'。请计算拖拉机可能在飞机上的不同位置的数量
最后结果(拖拉机在最终位置所面向的方向无关紧要)。
题目描述
Farmer John has just purchased a fancy new programmable tractor. To make the tractor move, he types in a string of length N
(1 <= N <= 100,000) consisting of only the characters F, L, and R. Each 'F' instructs the tractor to move forward one unit,
and the characters 'L' and 'R' result in left and right turns of 90 degrees, respectively. The tractor starts out at the origin (0,0)
facing north.
After programming his tractor by typing in his intended command string, FJ remembers that he typed exactly one character in
the command string incorrectly, but he can't remember which one!For example, he might have typed 'F' or 'L' when his intended
string contained the character 'R'. Please compute the number of different locations in the plane at which the tractor might
end up as a result (the direction the tractor faces in its final location does not matter).
Input format:
* Line 1: Farmer John's intended command string.
SAMPLE INPUT:
FF
INPUT DETAILS:
Farmer John wants the tractor to advance forward twice, ideally ending at position (0,2).
OUTPUT FORMAT:
* Line 1: The number of positions at which the tractor might end up,given that FJ mistypes one of the characters in his command string.
SAMPLE OUTPUT
3
OUTPUT DETAILS:
There are 4 possible mistyped sequences: FL, FR, LF, an RF. These will land the tractor at (0,1), (0,1), (-1,0), and (1,0) respectively,
a total of 3 distinct locations.
解析
由于我们只修改其中一个字符,所以问题就会变的简单许多。为了减少运算量,可以先预处理出从开始点到中间某一个点时的 \(x\)、\(y\) 和方向。同样预处理出从中间某一个点到终点的 \(x\)、\(y\) 和方向,在预处理时就把起点作为 \((0,0)\)。
对于修改第 \(k\) 个字符,可以把整段路径看作 \(1~k-1\)、\(k\)、\(k+1~len\) 三部分,第一部分和第三部分都预处理过了,只用合并就行了。方向的改变对应着坐标的变换,手推一下就行了。在合并的同时记录一下坐标,可以用 map
,我用的哈希表,能快一点。
氧气有毒、建议哈希乘数取131
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int hmod = 999983, hmax = 1e7 + 5, N = 2e5 + 5;
int len;
int way[4][2] = {{0, 1}, {-1, 0}, {0, -1}, {1, 0}};
int my[4][2] = {{1, 1}, {-1, 1}, {-1, -1}, {1, -1}};
string s;
long long ans;
struct dirt{ int dir, x, y; }ld[N], rd[N];
struct Hash_Table{
int h[hmod+1], nxt[hmax+1], cnt, vx[hmax+1], vy[hmax+1];
void Hash_in(int x, int y){
int hnum = (__int128)(x*131 + y) % hmod;
for(int i=h[hnum]; i; i=nxt[i])
if(vx[i] == x && vy[i] == y) return;
vx[++cnt] = x, vy[cnt] = y, nxt[cnt] = h[hnum], h[hnum] = cnt;
}
bool Hash_out(int x, int y){
int hnum = (__int128)(x*131 + y) % hmod;
for(int i=h[hnum]; i; i=nxt[i])
if(vx[i] == x && vy[i] == y) return 1;
return 0;
}
}h;
inline void merge(int a, char ch, int b){
int x = ld[a].x, y = ld[a].y, d = ld[a].dir;
if(ch == 'F') x += way[d][0], y += way[d][1];
else d = (d + (ch=='L'?1:-1) + 4) % 4;
if(d == 0) x += rd[b].x, y += rd[b].y;
else if(d == 1) x -= rd[b].y, y += rd[b].x;
else if(d == 2) x -= rd[b].x, y -= rd[b].y;
else x += rd[b].y, y -= rd[b].x;
if(!h.Hash_out(x, y)) ++ans, h.Hash_in(x, y);
}
int main(){
freopen("wrongdir.in", "r", stdin);
freopen("wrongdir.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>s; len = s.size();
for(int i=1; i<=len; ++i){
if(s[i-1] == 'F'){
ld[i].dir = ld[i-1].dir;
ld[i].x = ld[i-1].x + way[ld[i].dir][0];
ld[i].y = ld[i-1].y + way[ld[i].dir][1];
} else{
ld[i].dir = (ld[i-1].dir + (s[i-1]=='L'?1:-1) + 4) % 4;
ld[i].x = ld[i-1].x, ld[i].y = ld[i-1].y;
}
}
for(int i=len; i>0; --i){
if(s[i-1] == 'F'){
rd[i].dir = rd[i+1].dir;
rd[i].x = rd[i+1].x;
rd[i].y = rd[i+1].y + 1;
} else{
rd[i].x = rd[i+1].y;
rd[i].y = rd[i+1].x * -1;
if(s[i-1] == 'R') rd[i].dir = (rd[i-1].dir + 3) % 4;
else{
rd[i].x *= -1, rd[i].y *= -1;
rd[i].dir = (rd[i-1].dir + 1) % 4;
}
}
}
for(int i=1; i<=len; ++i){
if(s[i-1] == 'F') merge(i-1, 'L', i+1), merge(i-1, 'R', i+1);
else if(s[i-1] == 'R') merge(i-1, 'F', i+1), merge(i-1, 'L', i+1);
else merge(i-1, 'F', i+1), merge(i-1, 'R', i+1);
} return cout<<ans, 0;
}