ARC 114 题解

A

注意到 \(n\) 和值域很小,于是状压对前 \(15\) 个质数做一遍就好了。

#include <bits/stdc++.h>
typedef long long ll;
const int pr[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
int n,a[60];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	ll ans=1e18;
	for(int i=0;i<(1<<15);++i){
		ll pt=1;
		for(int j=0;j<15;++j)if(i&(1<<j)) pt=1ll*pr[j]*pt;
		bool fl=0;
		for(int j=1;j<=n;++j)if(std::__gcd(1ll*a[j],pt)==1){
			fl=1;
			break;
		}
		if(!fl)ans=std::min(ans,pt);
	}
	printf("%lld\n",ans);
	return 0;
}

B

\(f:S \rightarrow S\) 看做 \(f\)\(f(S)\) 连接一条有向边,最后会连出若干个环。

那么答案就是 \(2^{cnt}-1\),其中 \(cnt\) 表示环的数量。

#include <bits/stdc++.h>
typedef long long ll;
const int N=2e5+10;
const int mod=998244353;
int n,f[N],fa[N];
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
int fastpow(int a,int b){
	int ret=1;
	for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ret=1ll*ret*a%mod;
	return ret;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)fa[i]=i;
	for(int i=1;i<=n;++i)scanf("%d",&f[i]),fa[find(f[i])]=find(i);
	int cnt=0;
	for(int i=1;i<=n;++i)if(fa[i]==i)++cnt;
	int ans=(fastpow(2,cnt)-1+mod)%mod;
	printf("%d\n",ans);
	return 0;
}

C

考虑向序列中加入一个数 \(x\)\(f(A)\) 的贡献:

  • 如果 \(x\) 没有在序列中出现过,那么要一次操作来达到,贡献为 \(1\)

  • 如果 \(x\) 在序列出现过,记上一次出现位置为 \(lst\)\(x\) 出现位置为 \(p\)

    • 如果区间 \((lst,p)\) 中的数全部大于 \(x\),那么没有贡献。

    • 否则,贡献为 \(1\)

\(f(i,v)\) 表示序列长度为 \(i\),最后一个数为 \(v\) 对答案的贡献。

\(f(i,v)=m^{i-1} \displaystyle \sum_{j=1}^{i-1} (m-v)^{i-j-1} m^{j-1}\)

\(g(i)\) 表示长度为 \(i\) 的答案。

那么 \(g(i)=\displaystyle \sum_{v=1}^{m} g(i-1)+f(i-1,v)\)

固定 \(i\) 是,\(f(*,v)\) 可以 \(\mathcal{O}(nm)\) 递推,所以总复杂度为 \(\mathcal{O}(nm)\)

#include <bits/stdc++.h>
typedef long long ll;
const int N=5010;
const int mod=998244353;
int n,m;
ll pw[N],F[N],G[N];
int main(){
	scanf("%d%d",&n,&m);
	pw[0]=1;
	for(int i=1;i<=n;++i)pw[i]=1ll*m*pw[i-1]%mod;
	// for(int i=1;i<=n;++i)printf("%lld%c",pw[i]," \n"[i==n]);
	for(int j=1;j<=m;++j)F[j]=1;
	G[1]=m;
	for(int i=2;i<=n;++i){
		if(i>2){
			for(int j=1;j<=m;++j)F[j]=(1ll*(m-j)*F[j]%mod+pw[i-2])%mod;
			// for(int j=1;j<=m;++j)printf("%lld%c",F[j]," \n"[j==m]);
		}
		for(int j=1;j<=m;++j)G[i]+=(G[i-1]+(pw[i-1]-F[j]+mod)%mod)%mod,G[i]%=mod/*,printf("%lld\n",(pw[i-1]-F[j]+mod)%mod)*/;
		// printf("%lld\n",G[i]);
	}
	printf("%lld\n",G[n]);
	return 0;
}

D

看到这个翻转颜色,想到异或。

然后它是区间异或,于是把他转成异或差分。

所以我们可以处理出球的起始位置和结束位置的异或差分数组,那么此时每次可以选一个起点,一个终点匹配,求最小代价。

发现不太好做,那么把两个数组异或一下,把起点消掉,那么只要把这个序列全部消成 \(0\) 就好了。

把起点看作红球,终点看作蓝球,那么对于一段区间,我们可以让一个红球与一个蓝球匹配,然后消掉,也可以让两个红球匹配,然后消掉。

所以当起点数量小于终点数量或者起点数量减去终点数量不是 \(2\) 的倍数,那么无解。

不难发现不管是红球和蓝球匹配还是红球和红球匹配,都是与尽量近的匹配是更优的,所以排序一下,记 \(f(i,j)\) 表示前 \(i\) 个红球,匹配了 \(j\) 个蓝球的最小代价,分红球和蓝球转移。

#include <bits/stdc++.h>
typedef long long ll;
#define rep(i,l,r) for(int i(l);i<=int(r);++i)
#define per(i,l,r) for(int i(l);i>=int(r);i--)
const int N=5010;
int n,k,a[N],t[N],g[N],tot;
ll f[N][N];
std::map<int,int>mp;
int main(){
	// freopen("input.txt","r",stdin);
	scanf("%d%d",&n,&k);
	rep(i,1,n)scanf("%d",&a[i]),mp[a[i]]^=1;
	rep(i,1,k)scanf("%d",&t[i]),mp[t[i]]^=1;
	std::sort(a+1,a+n+1);
	for(auto i:mp)if(i.second)g[++tot]=i.first;
	std::sort(g+1,g+tot+1);
	if(n<tot||(n-tot)%2==1){
		puts("-1");
		return 0;
	}
	rep(i,1,n)rep(j,1,tot)f[i][j]=1e18;
	f[0][0]=0;
	rep(i,1,n){
		f[i][0]=f[i-1][0]+((i&1)?-a[i]:a[i]);
		// printf("%d\n",f[i][0]);
		rep(j,1,std::min(i,tot))if((i-j)%2==0){
			if(j>0)f[i][j]=std::min(f[i][j],f[i-1][j-1]+abs(a[i]-g[j]));
			if(i-2>=j)f[i][j]=std::min(f[i][j],f[i-2][j]+a[i]-a[i-1]);
		}
	}
	printf("%lld\n",f[n][tot]);
	return 0;
}

E

F

posted @ 2022-05-31 16:17  Nylch  阅读(64)  评论(2编辑  收藏  举报