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;
}