题解:AT_cf17_final_f Distribute Numbers
脑电波题,但是我似乎没有对上脑电波,搞了一个略微复杂的做法。
思路
我们可以将“任意两张纸上有且仅有一个相同的数字”转化为:遍历每种数字,找出这种数字出现的纸的集合,随后将这个集合内的纸两两连边,最后要求每两张纸之间恰好连了一条边。
那么显然有必要条件:
我们取出
我们绘制一个
我们发现合法的条件就是任意两列染色的行集合交集为
我们可以考虑先把前
对于后面的列我们可以考虑像下面这样一直染。
但是你发现染到第三组时就寄了。
自然地,我们可以考虑将第三组错开放,也就是这样:
这个所谓“错开放”的策略就是:
- 对于前
k 行我们每次放一段长度为k-1 的,与前面错开。 - 对于第
k+1 \sim 2k-1 行,我们每次放一段边长为k-1 的正方形的主对角线。 - 对于第
2k \sim n 行,我们每k-1 行分一组,每k-1 列分一组,都从0 开始编号。设当前列在列中是第i 组第j 个,要在第l 组行里染色,那么会染第l 组行的第(j+i \times l) \bmod (k-1) 个元素。
考虑一下这个策略何时会失效,也就是
显然有
当
所以我们取
可以取
代码
//A tree without skin will surely die.
//A man without face will be alive.
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;++i)
#define per(i,j,k) for (int i=j;i>=k;--i)
vector<int>vc[2005];int a[2005][2005];
inline bool prime(int x){
rep(i,2,x-1) if (x%i==0) return 0;
return 1;
}
inline void solve(){
rep(n,1000,2000) rep(k,1,n)
if (k*(k-1)==n-1 && prime(k-1)){
cout<<n<<' '<<k<<'\n';
int la=2;
vector<int>g;
rep(i,1,k){
vc[i].push_back(1),g.push_back(la);
rep(j,la,la+k-2) vc[i].push_back(j);
la=la+k-1;
}
int cnt=0,D=0;
rep(i,k+1,n){
int gadd=cnt;
for (auto it:g){
if (!vc[i].size()) vc[i].push_back(it+D);
else vc[i].push_back(it+gadd);
if (vc[i].size()>1) gadd+=D,gadd%=(k-1);
}
++cnt;
if (cnt==k-1) cnt=0,++D;
}
rep(i,1,n) for (auto j:vc[i])
a[j][++a[j][0]]=i;
rep(i,1,n){
rep(j,1,a[i][0]) cout<<a[i][j]<<' ';
cout<<'\n';
}
return;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t=1;
// cin>>t;
while (t--) solve();
return 0;
}