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 实现原理是哈希表,有一个固定的模数。。。

posted @ 2024-06-25 11:57  ppllxx_9G  阅读(51)  评论(0编辑  收藏  举报