====== Codeforces Round #732 (Div. 1) ======
[[https://codeforces.com/contest/1545|比赛链接]]
===== C. AquaMoon and Permutations =====
==== 题意 ====
给定 $2n$ 个 $1\sim n$ 的排列 $p_1,p_2\cdots p_{2n}$,其中有 $p_1,p_2\cdots p_n$ 构成拉丁方。
且对于 $k=1\sim n$,$p_k$ 与 $p_{k+n}$ 至少有一个元素相同,保证 $p_1,p_2\cdots p_{2n}$ 中不存在两个完全相同的排列。
输入给定打乱顺序后的 $2n$ 个排列,问有多少个 $n$ 子集可以构成拉丁方,同时输出任意一种合法方案。
==== 题解 ====
问题可以转化为有 $n\times n$ 个鸽巢,第 $(i,j)$ 个巢表示排列的第 $i$ 个位置为 $j$ 的情况。
每个排列 $P$ 可以视为 $n$ 个物品 $(1,P_1),(2,P_2)\cdots (n,P_n)$,于是问题等价于选择 $n$ 个排列使得每个巢恰好有一个物品。
接下来按下述流程操作 $n$ 次来构造合法方案,同时统计答案个数:
1、将剩余的所有排列的所有物品放入鸽巢。
2、如果某个巢中只有一个物品,则选定该物品对应的排列 $P$,同时删去与该排列在相同位置有相同数值的其他排列。
执行完删去操作后,已经不存在 $(1,P_1),(2,P_2)\cdots (n,P_n)$ 这 $n$ 中物品,于是至少可以删去 $(1,P_1),(2,P_2)\cdots (n,P_n)$ 这 $n$ 个鸽巢。
由于原来的 $p_1,p_2\cdots p_n$ 正好对应 $n^2$ 个鸽巢,且它们之间不会相互删除,所以上述操作恰好删去 $n$ 个鸽巢。
3、如果不存在这样的巢,假设已经选定了 $k$ 个排列,于是鸽巢还剩下 $n(n-k)$ 个。
每个鸽巢至少有两个物品,所以至少有 $2n(n-k)$ 个物品,对应至少有 $2(n-k)$ 个排列。
实际上,由于原来的 $p_i$ 和 $p_{i+n}$ 一定会相互删除,所以选定 $k$ 个排列则至少额外删除了 $k$ 个排列,于是至多有 $2(n-k)$ 个排列。
综上所述,剩下的排列恰好有 $2(n-k)$ 个,且每个鸽巢恰有两个物品。
$2(n-k)$ 个排列对应原来 $p_1,p_2\cdots p_n$ 中的 $n-k$ 个和 $p_{n+1},p_{n+2}\cdots p_{2n}$ 中的 $n-k$ 个。
而原来 $p_1,p_2\cdots p_n$ 中的 $n-k$ 个排列是拉丁方的一部分,所以恰好在 $n(n-k)$ 个鸽巢中各放一个物品。
由于每个鸽巢有恰两个物品,于是$p_{n+1},p_{n+2}\cdots p_{2n}$ 中的 $n-k$ 个排列也恰好在 $n(n-k)$ 个鸽巢中各放一个物品。
于是从这两组中任选一个最后都能组成拉丁方,方案数乘以 $2$。
每轮操作时间复杂度 $O(n^2)$,总时间复杂度 $O(n^3)$。
const int MAXN=505,mod=998244353;
int a[MAXN<<1][MAXN],b[MAXN],c[MAXN];
bool vis[MAXN<<1];
int main()
{
int T=read_int();
while(T--){
int n=read_int();
_for(i,0,n<<1){
_for(j,0,n)
a[i][j]=read_int()-1;
vis[i]=false;
}
int ans=1;
_for(k,0,n){
int pos=-1;
_for(i,0,n){
_for(j,0,n)
c[j]=0;
_for(j,0,n<<1){
if(vis[j])continue;
c[a[j][i]]++;
}
_for(j,0,n<<1){
if(vis[j])continue;
if(c[a[j][i]]==1){
pos=j;
break;
}
}
if(pos!=-1)
break;
}
if(pos==-1){
ans=(ans<<1)%mod;
_for(i,0,n<<1){
if(!vis[i]){
pos=i;
break;
}
}
}
b[k]=pos;
vis[pos]=true;
_for(i,0,n<<1){
if(vis[i])continue;
_for(j,0,n){
if(a[i][j]==a[pos][j]){
vis[i]=true;
break;
}
}
}
}
enter(ans);
_for(i,0,n)
space(b[i]+1);
puts("");
}
return 0;
}
===== D. AquaMoon and Wrong Coordinate =====
==== 题意 ====
初始有 $n$ 个人,第 $i$ 个人第 $t$ 秒的位置为 $x_i+v_it$。
接下来给定 $k$ 行,第 $i$ 行表示 $n$ 个人在第 $i-1$ 秒的位置,其中这 $n$ 个人的坐标顺序是打乱的。
这 $n\times k$ 个数据中有一个数据是错误的。求错误数据所在行号和正确的值。
==== 题解 ====
构造函数 $s1(t)=\sum_{i=1}^n x_i+v_it,s2(t)=\sum_{i=1}^n (x_i+v_it)^2$。
于是有 $s1(t+1)-s1(t)=\sum_{i=1}^n v_i$ 是固定值,利用该性质可以确定错误数据的行号。
接下来枚举错误数据的位置,根据 $s1$ 性质可以得到假定这个位置是错误的前提下的数据正确值。
由于 $s2(i+2)+s2(i)-s2(i+1)=\sum_{i=1}^n 2v_i$ 为确定值,利用该性质可以验证假设的正确性。总时间复杂度 $O(nk)$。
const int MAXN=1005;
int p[MAXN][MAXN];
LL s1[MAXN],s2[MAXN];
int main()
{
int n,k,row;
scanf("%d%d",&n,&k);
_for(i,0,k){
_for(j,0,n){
scanf("%d",&p[i][j]);
s1[i]+=p[i][j];
s2[i]+=1LL*p[i][j]*p[i][j];
}
}
_for(i,1,k){
if((s1[k-1]-s1[0])*i!=(s1[i]-s1[0])*(k-1)){
row=i;
break;
}
}
_for(i,0,n){
LL v=p[row][i]+(s1[k-1]-s1[0])/(k-1)*row-(s1[row]-s1[0]);
LL v2=s2[row]-1LL*p[row][i]*p[row][i]+v*v;
if(row>=3){
if(s2[2]+s2[0]-s2[1]*2==v2+s2[row-2]-s2[row-1]*2){
printf("%d %d\n",row,v);
fflush(stdout);
return 0;
}
}
else{
if(s2[k-1]+s2[k-3]-s2[k-2]*2==s2[row+2]+v2-s2[row+1]*2){
printf("%d %d\n",row,v);
fflush(stdout);
return 0;
}
}
}
return 0;
}