【题解】2020-2021 SWERC G.Decoration
题意
给定\(n,k\),构造一个长为\(k\)的数组\(s\)满足下列条件:(1) \(s_i\in [0,n-1]\) ,(2) \(s_{i+1}=s_i+\sigma(s_i)\ mod\ n\),(3) \(s_i\)互不相同。
若无解则输出-1,否则输出使得\(\sum_{i=1}^{k}s_i\)最小的方案。
\(1\le n,k\le 10^6\)
题解
由题意,每个数都有唯一的后继,记\(a\)的唯一后继为\(f(a)\),\(f^0(a)=a,f^{1+i}(a)=f(f^i(a))\),则相当于寻找满足上述条件且使得\(\sum_{i=0}^{k-1}f^i(a)\)最小的\(a\)。利用倍增\(O(n\log n)\)先求出每个元素的\(f^{2^i}(a)\),再求出\(\sum_{j=1}^{2^i}f^j(a)\)即可快速计算\(\sum_{i=0}^{k-1}f^i(a)\)。注意基环树上以某点为起点最长路的求法(Tarjan+拓扑排序会炸空间)。
#include <bits/stdc++.h>
#define pb(x) emplace_back(x)
using namespace std;
using P=pair<int,int>;
using ll=long long;
const int N=1000005;
int a[N],n,k;
int anc[N][20];
ll s[N][20];
int dep[N],vis[N];
void f1(){
scanf("%d%d",&n,&k);
if(k==1){printf("0");return ;}
for(int i=1;i<n;i++){
a[i]+=i;s[i][0]=i;
for(int j=i;j<n;j+=i){
++a[j];
}
}
for(int i=0;i<n;i++){a[i]%=n;anc[i][0]=a[i];s[i][0]=a[i];}
for(int i=0;i<n;i++){
int cnt=0,u=i,d=0;
vector<int> sta;
for(;;){
if(vis[u]==2){
d=dep[u];
break;
}
if(vis[u]==1){
d=cnt-dep[u];
for(;;){
int v=sta.back();sta.pop_back();
dep[v]=d;vis[v]=2;
if(v==u)break;
}
break;
}
dep[u]=cnt++;
vis[u]=1;sta.emplace_back(u);
u=a[u];
}
while(sta.size()>0){
++d;
int v=sta.back();sta.pop_back();
dep[v]=d;vis[v]=2;
}
}
for(int o=1;o<=19;o++){
for(int i=0;i<n;i++){
anc[i][o]=anc[anc[i][o-1]][o-1];
}
}
for(int o=1;o<=19;o++){
for(int i=0;i<n;i++){
s[i][o]=s[i][o-1]+s[anc[i][o-1]][o-1];
}
}
int ans=0;
ll res=1ll<<62;
for(int i=0;i<n;i++)if(dep[i]>=k){
int nk=k-1,u=i,tag=1;
ll na=i;
for(int o=19;o>=0;o--){
if((1ll<<o)<=nk){
nk-=(1ll<<o);
na+=s[u][o];
u=anc[u][o];
}
}
if(na<=res){
res=na;ans=i;
}
}
if(ans){
for(int i=1;i<=k;i++){
printf("%d ",ans);
ans=a[ans];
}
}
else{
printf("-1");
}
}
int main(){
f1();
return 0;
}