ARC112 F - Die Siedler 题解

题目链接:F - Die Siedler

题目大意:给定一个序列 \(a_1,a_2,\dots,a_n\),有 \(m\) 个长度为 \(n\) 的序列 \(s_{1,1},s_{1,2},\dots,s_{1,n},s_{2,1},s_{2,2},\dots,s_{2,n},\dots,s_{m,1},s_{m,2},\dots,s_{m,n}\),你可以将这 \(m\) 个序列中的每一个给 \(a\) 加任意多次,两个序列的加法定义为 \(a+b=\{a_1+b_1,a_2+b_2,\dots,a_n+b_n\}\)

在加完之后,对 \(a\) 作如下变换:若 \(a_i\ge 2\times i\),则令 \(a_i-=2\times i,a_{i\bmod n+1}+=1\),求 \(\sum_{i=1}^n a_i\) 的最小值。


题解:首先我们能够发现我们可以把所有的数堆在 \(a_1\) 上,具体操作就是将 \(a_1\) 变为 \(\sum_{i=1}^n a_i\times 2^{i-1}\times (i-1)!\),将 \(a_2,a_3,\dots,a_n\) 变为 \(0\),将 \(a_1\) 定义为序列的权值。

\(a\) 序列的权值为 \(C\),序列 \(\{s_i\}\) 的权值为 \(s_i\)

那么假设我们最后的序列的权值是 \(C^{\prime}\),那么可以得到 \(C^{\prime}=C+x_1\cdot s_1+x_2\cdot s_2+\cdots x_m\cdot s_m\)

但是我们发现如果按照题目意思一次操作 \(1,2,\dots,n\) 可以使 \(a_1\) 减去 \(2^n n!-1\),所以 \(C^{\prime}=C+x_1\cdot s_1+x_2\cdot s_2+\cdots x_m\cdot s_m-y\cdot (2^n n!-1)\)

由裴蜀定理可得 \(\gcd(s_1,s_2,\dots,s_m,2^n n!-1)|(C^{\prime}-C)\)

那么令 \(d=\gcd(s_1,s_2,\dots,s_m,2^n n!-1)\)

接下来我们有两种做法:

  1. 直接枚举 \(C\bmod d+kd\) 中的 \(k\),因为 \(d\)\(2^n n!-1\) 的因数,所以我们只需要枚举到 \(2^n n!-1\) 就可以了,时间复杂度 \(O(\frac{2^n n!}{d})\)
  2. 考虑同余最短路,\(i\)\(i+2^{j-1}(j-1)!\) 连长度为 \(1\) 的边,最后输出 \(dis_{C\bmod d}\),时间复杂度 \(O(nd)\)

所以时间复杂度为 \(O(\min(nd,\frac{2^n n!}{d}))\),打表发现可以过。

代码:

#include <cstdio>
#include <algorithm>
typedef long long ll;
ll gcd(ll a,ll b){
	if(b==0){
		return a;
	}
	return gcd(b,a%b);
}
const int Maxn=16;
const int Maxm=50;
const int Maxd=1500000;
const int Inf_int=0x3f3f3f3f;
int n,m;
ll pow_fac[Maxn+5];
ll C;
ll read_val(){
	ll ans=0;
	for(int i=0;i<n;i++){
		int val;
		scanf("%d",&val);
		ans+=val*pow_fac[i];
	}
	return ans;
}
int find_ans(ll s){
	int ans=0;
	for(int i=1;i<=n;i++){
		ans+=s%(2*i);
		s/=2*i;
	}
	return ans;
}
int dis[Maxd+5];
int qu[Maxd+5],qu_f,qu_t;
int main(){
	pow_fac[0]=1;
	for(int i=1;i<=Maxn;i++){
		pow_fac[i]=pow_fac[i-1]*2*i;
	}
	scanf("%d%d",&n,&m);
	ll d=pow_fac[n]-1;
	C=read_val();
	for(int i=1;i<=m;i++){
		d=gcd(d,read_val());
	}
	if(pow_fac[n]/d<d){
		int ans=Inf_int;
		for(ll i=(C%d==0?d:C%d);i<pow_fac[n];i+=d){
			ans=std::min(ans,find_ans(i));
		}
		printf("%d\n",ans);
	}
	else{
		qu_f=1,qu_t=0;
		for(int i=0;i<n;i++){
			int u=pow_fac[i]%d;
			if(dis[u]==0){
				dis[u]=1;
				qu[++qu_t]=u;
			}
		}
		int T=C%d;
		while(qu_f<=qu_t&&dis[T]==0){
			int u=qu[qu_f++];
			for(int i=0;i<n;i++){
				int v=(u+pow_fac[i])%d;
				if(dis[v]==0){
					dis[v]=dis[u]+1;
					qu[++qu_t]=v;
				}
			}
		}
		printf("%d\n",dis[T]);
	}
	return 0;
}
posted @ 2021-11-11 09:57  with_hope  阅读(155)  评论(0编辑  收藏  举报