Nastia and a Hidden Permutation
题目
娜斯佳有一个长度为 \(n\) 的隐藏排列 \(p\),由从 \(1\) 到 \(n\) 的整数组成。出于某种原因,你想找出这个排列。为此,您可以给她一个整数 \(t\) \(( 1 \le t \le 2 )\),两个不同的索引 \(i\) 和 \(j\) \(( 1 \le i, j \le n ,i \neq j )\),以及一个整数 \(x\) \(( 1 \le x \le n - 1 )\)。
根据 \(t\),她将给出答案:
- \(t = 1\) : \(\max{(\min{(x, p_i)}, \min{(x + 1, p_j)})}\)
- \(t = 2\) : \(\min{(\max{(x, p_i)}, \max{(x + 1, p_j)})}\)
您最多可以询问娜斯佳 \(\lfloor \frac {3 \cdot n} { 2} \rfloor + 30\) 次。保证她不会因为你的询问而改变她的排列。你能猜出排列组合吗?
分析
考虑用 \(t=2,i,i+1,1\) 问出 \(1\) 的位置。
当答案等于 \(2\) 时 \(p_i,p_{i+1}\) 中必有 \(1\) 或 \(2\);当答案等于 \(1\) 时则 \(p_i = 1\)。
此时考虑使用 \(t=1,\text{1的位置},x,n-1\) 对每个位置询问,得出答案。
#include<bits/stdc++.h>
//#define int long long
int read() {int x=0; bool f=false; char ch=(char)getchar();while(! isdigit(ch)) f|=(ch == '-'),ch=(char)getchar();while(isdigit(ch)) x=x*10+(ch^48),ch=(char)getchar();return f ? -x : x;
}
using namespace std;
const int N=1e4+10;
int a[N],n;
void query(int t,int i,int j,int x) {cout<<"? "<<t<<" "<<i<<" "<<j<<" "<<x<<endl;
}
void sol() {n=read(); int tmp = 0;for(int i=1;i<=n;i+=2) {query(2,i,i%n+1,1);int op=read();if(op == 1) tmp=i;if(op == 2) {query(2,i%n+1,i,1);int x=read();if(x == 1) tmp=i%n+1;}}a[tmp]=1;for(int i=1;i<=n;i++) {if(i == tmp) continue ;query(1,tmp,i,n-1); a[i]=read();}cout<<"!";for(int i=1;i<=n;i++) printf(" %d",a[i]);puts("");fflush(stdout);
}
signed main() {int T=read();while(T -- ) sol();return 0;
}
Armchairs
题目
有 \(n\) 把扶手椅,从左到右编号为 \(1\) 到 \(n\)。有些扶手椅上有人(每把扶手椅上最多有一个人),有些扶手椅上没有人。有人坐的扶手椅数量不大于 \(\frac{n}{2}\)。
出于某种原因,你想让人们从自己的扶手椅上换到其他扶手椅上。如果 \(i\) 把扶手椅有人坐,而 \(j\) 把扶手椅没人坐,那么你可以让坐在 \(i\) 把扶手椅上的人搬到 \(j\) 把扶手椅上。一个人从 \(i\) 把扶手椅移动到 \(j\) 把扶手椅所需的时间是 \(|i - j|\) 分钟。您可以多次执行这个操作,但是这些操作必须按顺序进行,也就是说,在您上一次要求移动的人移动到他们的目的地扶手椅之前,您不能告诉别人移动。
您希望实现以下情况:每个最初被占用的座位都必须空出来。最少需要多长时间才能做到?
分析
考虑一个模拟费用流里面常见的结论,一个人到一把椅子与另一个人到另一把椅子,路线是不会交叉的,交叉一定不优。
意识到背后一定有一个费用流模型后,考虑一下 \(dp\) 吧。
\(f_{i,j}\) 表示第 \(i\) 个人挪到第 \(j\) 位置的最小代价。
#include<bits/stdc++.h>
//#define int long long
int read() {int x=0; bool f=false; char ch=(char)getchar();while(! isdigit(ch)) f|=(ch == '-'),ch=(char)getchar();while(isdigit(ch)) x=x*10+(ch^48),ch=(char)getchar();return f ? -x : x;
}
using namespace std;
const int N=5e3+10;
int f[N][N],n,a[N],p[N],cnt;
signed main() {memset(f,0x3f,sizeof f);n=read();for(int i=1;i<=n;i++) {a[i]=read(); if(a[i]) p[++cnt]=i;}for(int i=0;i<=n;i++) f[0][i]=0;for(int i=1;i<=cnt;i++)for(int j=1;j<=n;j++) {if(a[j]) f[i][j]=f[i][j-1];else f[i][j]=min(f[i][j-1],f[i-1][j-1]+abs(p[i]-j));}printf("%d\n",f[cnt][n]);return 0;
}
Robot Collisions
题目
有 \(n\) 个机器人沿 OX 轴行驶。还有两面墙:一面位于坐标 \(0\) 处,另一面位于坐标 \(m\) 处。
\(i\) -th 机器人从整数坐标 \(x_i (0 < x_i < m )\) 开始,以每秒 \(1\) 个单位的速度向左(朝向 \(0\))或向右移动。没有两个机器人从同一个坐标开始。
每当一个机器人到达一堵墙时,它就会立即掉头,并以相同的速度朝相反的方向继续前进。
每当几个机器人在同一整数坐标处相遇,它们就会相撞并爆炸成灰尘。一旦一个机器人爆炸,它就不会再与其他机器人相撞。请注意,如果多个机器人在非整数坐标处相遇,则不会发生任何事情。
查找每个机器人是否爆炸,如果爆炸,则打印爆炸时间,否则打印 \(-1\)。
分析
我写不动了,我要开始口胡了。
注意到奇偶性相同的机器人才会相撞,奇偶性不同的永远只会擦肩而过。
再考虑在左边向右的和在右边向左的肯定先撞,括号序 \(LR\)。
这样操作完,剩下的序列是 \(LLL \cdots RRR\) 这个样式的,此时左边第一个肯定与左边第二个相撞,左边第三个与左边第四个相撞,以此类推。