Loading

ARC112F 做题记录

link

主要记录一下这种题的做题过程。

  • \(\text{Step 1}\)

我们发现第 \(j\) 种牌每满 \(2j\) 张就会向下一位进一,考虑套路:我们用第 \(1\) 种牌来表示第 \(j\) 种牌,权值为 \(W_{j-1}=\prod\limits_{i=1} ^{j-1} 2i\)

这样我们可以只用第 \(1\) 种牌的数量,即一个数来代替一组牌,不妨设初始数字为 \(x\)。注意可以用第 \(n\) 种牌换第 \(1\) 种,用一个数表述就是把这个数减去 \(W_n-1=\prod\limits_{i=1}^n 2i-1\),限制为不能减成 \(0\)

容易发现一个数对应了唯一一组卡牌,每组卡牌都有唯一一个表示的数,这样我们就完成了类似于双射的转化。

考虑每次加入一个卡包,类似的,用一个数表示每种卡包,不妨设为 \(b_{1...m}\),那么我们可以每次给 \(x\) 加上 \(b\) 中的一个数。

  • \(\text{Step 2}\)

\(x>W_n-1\) 时,我们还可以给 \(x\) 减去 \(W_n-1\)

我们用 \(x_i\) 表示加入卡包 \(i\) 的数量,用 \(y\) 表示减去 \(W_n-1\) 的次数,有

\[x'=x+\sum_{i=1}^m x_ib_i -y(W_n-1) \]

根据裴蜀定理,有解当且仅当 \((x'-x)|\gcd(b_1,b_2,...,b_m,W_n-1)\)

而且对于方程 \(\sum\limits_{i=1}^m x_ib_i-y(W_n-1)=\gcd(b_1,b_2,...,b_m,W_n-1)\),我们一定可以构造出满足 \(x_1,x_2,..,x_m,y\ge 0\) 的一组解。

\(d=\gcd(b_1,b_2,...,b_m,W_n-1)\),设 \(f(S)\) 表示数字 \(S\) 贪心摊开后的卡牌数总和。一种暴力的方法是直接从最小的可能的 \(x'\) 开始枚举,每次加上 \(d\),取 \(f(x')\) 的最小值。

  • \(\text{Step 3}\)

但是当 \(d\) 很小的时候会爆炸,我们考虑另一种可能的暴力,然后根号分治平衡两种做法

考虑数字 \(x\) 在模 \(d\) 意义下可以互相到达,我们只需要找出模 \(d\) 意义下等于 \(x\) 的任意一个数字 \(x'\) 的最小 \(f(x')\)

直接从答案入手,我们考虑最终 \(f(x')\) 的局面,枚举每一种牌的数量,然后找出合法的最小答案。

考虑同于最短路,在模 \(d\) 意义下进行,每次增加一张牌,并算出对应的 \(x'\)

两种做法中前者 \(O(\dfrac{2^nn!}d)\),后者 \(O(nd)\),平衡一下是 \(O(\sqrt{2^nn!})\)。(\(W_n=2^nn!\)

接下来是魔法:由于 \(d\)\(2^nn!-1\) 的因数,打表可得 \(\sqrt{2^nn!}\) 范围内 \(d\) 最大 \(1214827\)

总结:这种题应该多思考如何通过操作特点来转化,把复杂的东西转化成易于观察的东西。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
using namespace std;
const ll maxn=110;
ll n,m,c[maxn],a[maxn][maxn],b[maxn],x,d,dis[maxn*20000],q[maxn*20000],l,r,w[maxn];
ll calc(ll k){
	ll s=0;
	for(ll i=1;i<=n;i++){
		s+=k%(2*i); k/=2*i;
	} return s;
}
ll gcd(ll a,ll b){
	if(!b) return a;
	return gcd(b,a%b);
}
int main(){
	scanf("%lld%lld",&n,&m);
	w[0]=1;
	for(ll i=1;i<=n;i++) w[i]=w[i-1]*2*i;
	ll t=w[n]-1;
	for(ll i=1,v=1;i<=n;i++)
		scanf("%lld",c+i), x+=w[i-1]*c[i], x=(x-1)%t+1;
	for(ll i=1;i<=m;i++){
		for(ll j=1,v=1;j<=n;j++)
			scanf("%lld",a[i]+j), b[i]+=w[j-1]*a[i][j], b[i]%=t;
	} d=t;
	for(ll i=1;i<=m;i++)
		d=gcd(d,b[i]);
	if(d>t/d){ ll ans=6e18;
		for(ll i=(x-1)%d+1;i<=t;i+=d)
			ans=min(ans,calc(i));
		printf("%lld",ans);
	} else{
		l=1, r=0;
		memset(dis,-1,sizeof dis);
		for(ll i=1;i<=n;i++) dis[w[i-1]%d]=1;
		for(ll i=0;i<d;i++)
			if(dis[i]!=-1) q[++r]=i;
		while(l<=r){
			ll u=q[l++];
			for(ll i=1;i<=n;i++){
				ll v=(u+w[i-1])%d;
				if(dis[v]==-1){
					dis[v]=dis[u]+1;
					q[++r]=v;
				}
			}
		}
		printf("%lld",dis[x%d]);
	}
	return 0;
}
posted @ 2024-03-28 15:42  Lgx_Q  阅读(10)  评论(0编辑  收藏  举报