【题解】2020-2021 SWERC G.Decoration

link

题意

给定\(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;
}

posted @ 2021-10-07 08:57  bobh  阅读(292)  评论(0)    收藏  举报