Codeforces Round 954 (Div. 3)
T1,T2水题,没啥好说的。T3半水题,T4赛时唐氏,T5有一点想不到。
T3 Update Queries
题意
给一个长度为 \(n\) 的字符串 \(s\),一个长度为 \(m\) 的字符串 \(c\),以及 \(m\) 个指针 \(ind_i\)。
你可以任意更改字符串 \(c\) 和指针的顺序,然后按顺序执行以下操作:\(s_{ind_i}=c_i\)。
求 \(m\) 次操作后可以得到的字典序最小的字符串 \(s\)。(注意必须从第一次到最后一次执行 \(m\) 次操作)
解析
一眼看上去有点麻烦,想一想发现对于同一个 \(ind\),后面的更新会覆盖前面的,所以其实只要记最小的对应的 \(c_i\),
又因为 \(c\) 的顺序可以任意改变,所以实际上就是将 \(ind_i\) 排序,然后每次取最小的 \(c_i\)。如果重复出现的 \(ind_i\) 直接跳过。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5+5;
int t;
int n,m,a[N];
bool vs[N];
string s,c;
int main()
{
scanf("%d",&t);
while(t--)
{
memset(vs,0,sizeof(vs));
scanf("%d%d",&n,&m);
cin>>s; s=' '+s;
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
cin>>c; c=' '+c;
sort(a+1,a+1+m);
sort(c.begin(),c.end()); int l=1;
for(int i=1;i<=m;i++)
{
if(vs[a[i]]) continue;
s[a[i]]=c[l++]; vs[a[i]]=1;
}
for(int i=1;i<=n;i++) printf("%c",s[i]);
puts("");
}
return 0;
}
T4 Mathematical Problem
题意
给一个长度为 \(n\) 的由 \(0\) 到 \(9\) 数字组成的字符串,你可以在数字之间插入 \(n-2\) 个 \(\times\) 或 \(+\),得到一个有效的运算式。
注意 \(09\) 是合法的,并被看作 \(9\)。例如 \(+9 \times 8 \times 70+09\)(符号只能放在数字中间)、\(98 \times 70+0+9\)(必须恰好有 \(4\) 个符号)是非法的。
运算式的结果是根据数学规则计算的——首先进行所有乘法运算,然后进行加法运算。请你找到最小的结果。
解析
赛时唐氏,一眼数据范围然后开始区间 dp,更唐氏的是赛时暴力处理乘积,\(O(n^5)\) 最后调出来还过了!!!
赛后优化到 \(O(n^4)\),感觉还挺快的。
$n^4$ code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 25;
int t,n,a[N];
LL f[N][N],ans;
int work()
{
int b[N]; ans=1e9;
for(int i=1;i<n;i++)
{
memset(f,0x3f,sizeof(f));
int o=1,p=1;
while(o<=n)
{
if(o==i) b[o]=a[p]*10+a[p+1],p+=2;
else b[o]=a[p++]; o++;
}
for(int j=1;j<n;j++) f[j][j]=b[j];
for(int len=2;len<n;len++)
{
for(int l=1,r=l+len-1;r<n;r++,l++)
{
LL tmp=1;
for(int k=l;k<=r;k++)
{
tmp*=b[k];
if(k==r) f[l][r]=min(f[l][r],tmp);
else f[l][r]=min(f[l][r],tmp+f[k+1][r]);
if(tmp>ans) break;
}
}
}
ans=min(ans,f[1][n-1]);
}
return ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n); bool fl=0;
for(int i=1;i<=n;i++) scanf("%1d",&a[i]),fl|=(!a[i]);
if(fl&&n>3) puts("0");
else printf("%d\n",work());
}
return 0;
}
正解是贪心,因为要放 \(n-2\) 个计算符,所以一定有两个数要拼凑一下。
\(n=2\) 特判一下,\(n>2\) 时先枚举所有拼凑方案,
对于得到的 \(n-1\) 个数,如果是 \(1\) 直接跳过(可以乘),因为剩下的数都比 \(1\) 大,所以加法一定比乘法小,因此贪心策略就是直接加。
注意如果全是 \(1\) 结果应为 \(1\)。
#include<bits/stdc++.h>
using namespace std;
const int N = 21;
int t,n,a[N],b[N];
int work()
{
int ans=1e9;
for(int i=1;i<n;i++)
{
int res=0;
for(int j=1,p=1;j<n;j++,p++)
{
if(p==i) b[j]=a[p]*10+a[p+1],p++;
else b[j]=a[p];
}
for(int j=1;j<n;j++)
{
if(b[j]==0) return 0;
if(b[j]!=1) res+=b[j];
}
res=max(1,res);
ans=min(ans,res);
}
return ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%1d",&a[i]);
if(n==2) printf("%d\n",a[1]*10+a[2]);
else printf("%d\n",work());
}
return 0;
}
T5 Beautiful Array
题意
给 \(n\) 个数字和 \(k\),每次操作可以将任意一个数加 \(k\),
现在你可以将这 \(n\) 个数任意排列,求让它变成一个回文序列的最小操作次数。
如果不能输出 \(-1\)。
回文序列定义为 \(b_i=b_{n-i+1} \ \ (1 \le i \le n)\)。
解析
我们按模数分组(需要离散化),如果两个数 $mod \ k $ 相等,那么他们两个可以通过操作变成相等的数。
因此我们分类后排序,每组相邻的两个数之差 \(\div k\) 就是需要操作数,求和即可。
要求最多有一组个数为奇数,才能满足一一配对,否则不可能满足。
问题在奇数时,因为奇数中间的数不用配对,所以我们在计算时应该删掉一个。
问题就是去掉哪一个数最优,首先,这个数一定是排序后奇数位上的数。
然后我们先假定去掉这一组中的第一个数,计算出一个值。
每一次更改其实就是进行 \(-(v[i+1]-v[i])+(v[i]-v[i-1])\),也就是将我们选取的两个点向前挪一位。
变成
因此我们可以 \(O(n)\) 处理。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5+5;
int t;
int n,k,a[N];
vector<int> v[N];
map<int,int> mp;
int main()
{
scanf("%d",&t);
while(t--)
{
LL ans=0;
int tot=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
int tmp=a[i]%k;
if(mp.find(tmp)==mp.end()) mp[tmp]=++tot;
v[mp[tmp]].push_back(a[i]);
}
int fl=0;
for(int i=1;i<=tot;i++)
{
sort(v[i].begin(),v[i].end());
if(v[i].size()&1)
{
fl++;
if(fl==2)
{
puts("-1");break;
}
LL tmp=0;
for(int j=2;j<v[i].size();j+=2) tmp+=(v[i][j]-v[i][j-1])/k;
LL res=tmp;
for(int j=1;j<v[i].size();j+=2)
{
tmp=tmp-(v[i][j+1]-v[i][j])/k+(v[i][j]-v[i][j-1])/k;
res=min(res,tmp);
}
ans+=res;
}
else
for(int j=1;j<v[i].size();j+=2) ans+=(v[i][j]-v[i][j-1])/k;
}
if(fl<2) printf("%lld\n",ans);
for(int i=1;i<=tot;i++) v[i].clear();
mp.clear();
}
}
CF 不能用 unordered_map
据 qinyun 悲惨经历,CF 用 unordered_map
真的会被 \(\mathbb{Hack}\),
unordered_map
实现原理是哈希表,有一个固定的模数。。。