Lynkcat
2021-12-23 15:25:43
学习这玩意是因为补题 IOI2022 集训队互测 Day5T2。
题面是这样的:
现有
可以说明,在给定的范围内一定有解。
一行一个正整数
vp 时只写了前面的
学长找到了一份名字叫 sts 的课件,于是我点开来看,是一份英文课件,直到看完这份课件,我终于会了这道题。觉得这道题很有意思,于是写了这篇博文。
本文的内容大部分来源于此英文课件,但由于我并不知道其具体出处,无法在此标注。
一个 Steiner 三元系由一个集合
比如
容易算出如果
关于它的一个结论是,
因为包含
当
当
拉丁方阵是一个
例如
123
231
312
求一个拉丁方阵非常容易,最常用的方法是对于每个数,随机选取方阵中一个空的格子,初始位置是
拟群(quasi-group)是一种代数系统。
拟群由一个集合
满足以下两个条件:
举个例子
0 | 1 | 2 | |
---|---|---|---|
0 | 1 | 2 | 0 |
1 | 0 | 1 | 2 |
2 | 2 | 0 | 1 |
幂等拟群是指满足
一个拟群是可交换的是指满足
看到这里也许你会发现,拟群的乘法表实际上就是一个拉丁方阵。
而幂等拟群可以用一个满足
而可交换这一性质可以由
当
1 3 2
3 2 1
2 1 3
1 4 2 5 3
4 2 5 3 1
2 5 3 1 4
5 3 1 4 2
3 1 4 2 5
可以发现我们只要选取的初始位置是主对角线上
1 3 2 4
3 2 4 1
2 4 1 3
4 1 3 2
1 4 2 5 3 6
4 2 5 3 6 1
2 5 3 6 1 4
5 3 6 1 4 2
3 6 1 4 2 5
6 1 4 2 5 3
构造方法是,对于前
我们将前
设
同样来分析下正确性:
第一种三元组个数为
接下来只要证明每对元素都在其中出现过即可。
(下文
当其中一个元素是
对于
如果
其他情况均可类似证明在第三种出现过。
所以在代码实现中,我们只需要构造一个 可交换的半幂等拟群的乘法表(拉丁方阵),然后构造如上的三元组即可。代码难度同样很小。
这题在构造时需要充分利用拟群的定义才能得出思路,包括
#include<bits/stdc++.h>
#define poly vector<int>
#define IOS ios::sync_with_stdio(false)
#define ll long long
#define mp make_pair
#define mt make_tuple
#define pa pair < int,int >
#define fi first
#define se second
#define inf 1e18
#define mod 998244353
//#define int ll
#define N 3005
using namespace std;
inline char gc(){static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
#define gc getchar
inline ll read(){char c=gc();ll su=0,f=1;for (;c<'0'||c>'9';c=gc()) if (c=='-') f=-1;for (;c>='0'&&c<='9';c=gc()) su=su*10+c-'0';return su*f;}
inline void write(ll x){if (x<0){putchar('-');write(-x);return;}if (x>=10) write(x/10);putchar(x%10+'0');}
inline void writesp(ll x){write(x),putchar(' ');}
inline void writeln(ll x){write(x);putchar('\n');}
int id[N][5];
int p;
int a[N][N];
int n;
vector<tuple<int,int,int>>ans;
void Init(int x)//构造可交换的幂等拟群
{
for (int i=1;i<=x;i++)
for (int j=1;j<=3;j++)
id[i][j]=(i-1)*3+j;
for (int i=1;i<=x;i++)
{
int nowx=i,nowy=i;
while (!a[nowx][nowy])
{
a[nowx][nowy]=i;
nowx--;
nowy++;
if (nowx==0) nowx=x;
if (nowy==x+1) nowy=1;
}
}
}
void Init1(int x)//构造可交换的半幂等拟群
{
for (int i=1;i<=x;i++)
for (int j=1;j<=3;j++)
id[i][j]=(i-1)*3+j;
p=n;
for (int i=1;i<=x/2;i++)
{
int nowx=i,nowy=i;
while (!a[nowx][nowy])
{
a[nowx][nowy]=i;
nowx--;
nowy++;
if (nowx==0) nowx=x;
if (nowy==x+1) nowy=1;
}
}
for (int i=x/2+1;i<=x;i++)
{
int nowx=i-x/2+1,nowy=i-x/2;
while (!a[nowx][nowy])
{
a[nowx][nowy]=i;
nowx--;
nowy++;
if (nowx==0) nowx=x;
if (nowy==x+1) nowy=1;
}
}
}
void outp()
{
for (auto u:ans) writesp(get<0>(u)),writesp(get<1>(u)),writeln(get<2>(u));
}
void BellaKira()
{
n=read();
if (n%6==3)
{
Init(n/3);
for (int i=1;i<=n/3;i++)
for (int j=i;j<=n/3;j++)
{
if (i==j)
ans.push_back(mt(id[i][1],id[i][2],id[i][3]));
else
{
for (int k=1;k<=3;k++)
ans.push_back(mt(id[i][k],id[j][k],id[a[i][j]][(k)%3+1]));
}
}
outp();
} else
{
Init1((n-1)/3);
for (int i=1;i<=n/3;i++)
{
for (int j=i;j<=n/3;j++)
{
if (i==j&&i<=n/6)
ans.push_back(mt(id[i][1],id[i][2],id[i][3]));
else
if (i!=j)
{
for (int k=1;k<=3;k++)
ans.push_back(mt(id[i][k],id[j][k],id[a[i][j]][(k)%3+1]));
}
}
if (i<=n/6)
{
for (int k=1;k<=3;k++)
ans.push_back(mt(p,id[i][k],id[n/6+i][(k-1+3-1)%3+1]));
}
}
outp();
}
}
signed main()
{
int T=1;
while (T--)
{
BellaKira();
}
}
/*
*/