solution of 2225G
RainFestival · · 题解
Update on 2026/04/23: 修改了一处时间复杂度分析的错误。
题意:构造
首先我们发现,在
在
我们观察一下这个问题的性质。首先,钦定一个
具体的,假设这个剩余类为
这样我们就把问题转化为一个由
::::info[简要证明] 首先我们考虑一个更简要的做法。这个做法在理论上的正确性更加明显,但是笔者认为它在实现上比较复杂。
我们发现,这个问题是大致递归的。记录剩余类数量为
然后说说为什么之前的做法是对的。我们可以考虑一下按照原方法连成图的结构。它有一些冗余边,因此我们需要说明那些冗余边不会被访问到,由此,在正确性上可以等价于刚才介绍的那个做法。我们可以把它看作一个分层图,约定更前面的
在
时间空间复杂度
::::info[小小优化,不改变时空复杂度]
首先,我们选取最小的那个
其次,我们在求
下面是笔者的
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cassert>
#include<utility>
#include<vector>
#include<cmath>
struct edge{int to,id;edge(int to,int id){this->to=to,this->id=id;}};
std::vector<edge> a[5005];
int t,n,m,vis[5005],p[20],base,now;
std::vector<std::pair<int,int>> s;
std::vector<int> f[5005];
void add(int x,int y,int id){a[x].push_back(edge(y,id));}
void dfs(int v,int id)
{
vis[v]=1;
s.push_back(std::make_pair(v,id));
for (int i=0;i<(int)a[v].size();i++)
{
int u=a[v][i].to;
if (vis[u]) continue;
dfs(u,a[v][i].id);
break;
}
}
void print(int x,int id)
{
if (!id)
{
for (int i=1;i<(int)f[x].size();i++) printf("%d ",f[x][i]);
printf("%d ",f[x][0]);
}
else
{
int lst=((x-p[id])%base+base)%base;
int fst=f[lst][0]+p[id];
assert(fst<n);
printf("%d ",fst);
for (int i=1;i<(int)f[x].size();i++) if (f[x][i]!=fst) printf("%d ",f[x][i]);
printf("%d ",f[x][0]);
}
}
int main()
{
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) scanf("%d",&p[i]);
std::sort(p+1,p+m+1);
int g=p[1];
for (int i=1;i<=m;i++) g=std::__gcd(g,p[i]);
if (g!=1) {puts("-1");continue;}
base=p[1],now=p[1];
for (int i=0;i<base;i++) f[i].clear(),a[i].clear();
for (int i=0;i<n;i++) f[i%base].push_back(i);
for (int i=2;i<=m;i++)
{
int t=p[i]%now;
if (t==0) continue;
for (int k=0;k<base;k++) add(k,(k+p[i])%base,i);
now=std::__gcd(now,p[i]);
}
s.clear();
for (int i=0;i<base;i++) vis[i]=0;
dfs(0,0);
assert((int)s.size()==base);
for (int i=0;i<base;i++) print(s[i].first,s[i].second);
puts("");
}
return 0;
}