Codeforces Round #739 (Div. 3)

Posted Maystern

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #739 (Div. 3)相关的知识,希望对你有一定的参考价值。

Codeforces Round #739 (Div. 3)

A - Dislike of Threes

\\(t(1\\leq t \\leq 100)\\)组数据,每组数据给出一个\\(k(1\\leq k \\leq 1000)\\)

求出第\\(k\\)小的正整数满足不被\\(3\\)整除,数的结尾不是\\(3\\)

可以发现,按照上述方式构造的数,大致是线性分布,因此

考虑\\(O(k)\\)预处理,\\(O(1)\\)查询。

# include<bits/stdc++.h>
using namespace std;
bool check(int x){
	if (x%3==0) return 0;
	if (x%10==3) return 0;
	return 1;
}
int main()
{
	int now=1;
	vector<int>a;
	while (1) {
		if (check(now)) a.push_back(now);
		now++;
		if (a.size()>1000) break;
	} 
	int t; scanf("%d",&t);
	while (t--) {
		int x; scanf("%d",&x);
		printf("%d\\n",a[x-1]);
	}
	return 0;
} 

B - Who\'s Opposite?

\\(t(1\\leq t \\leq 10^4)\\)组数据,每组数据给出\\(a,b,c (1\\leq a,b,c \\leq 10^8)\\)

考虑在一个由\\(1-n\\)顶点按照顶点编号顺时针排列的环上,\\(n\\)为偶数。

顶点\\(a\\)和顶点\\(b\\)构成的直线恰好是对称轴,若顶点\\(c\\)和顶点\\(d\\)构成的直线也恰好是对称轴。

求出顶点\\(d\\)的值,若输入数据错误,不存在\\(d\\),输出\\(-1\\)

考虑将所有顶点编号都减去\\(1\\),所以顶点编号是\\(0-(n-1)\\)这样可以更方便的使用整除的性质。

\\(a\' = a-1,b\'=b-1,c\'=c-1,d\'=d-1\\)

显然,利用\\(a\',b\'\\)可以求出\\(n = 2\\times |a\'-b\'|\\).

而$c\'-d\' ≡ a\'-b\' \\pmod{n} $

因此\\(d\' = c\'-a\'+b\' \\pmod{n}\\) 所以 \\(d = d\'+1\\)

不符合的条件输出\\(-1\\),就是 \\(a\',b\',c\'\\)中存在一个,大于等于\\(n\\),说明输入错误。

# include<bits/stdc++.h>
using namespace std;
int solve(int a,int b,int c) {
	int n=abs(a-b)*2;
	if (a>=n||b>=n||c>=n) return -1;
	return (((c-a)%n+n)%n+b)%n+1;
}
int main()
{
	int t; scanf("%d",&t);
	while (t--) {
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		a--;b--;c--;
		int ans = solve(a,b,c);
		printf("%d\\n",ans);
	}
	return 0;
} 

C - Infinity Table

\\(t(1\\leq t \\leq 100)\\)组数据,每组数据给出一个\\(k(1\\leq k \\leq 10^9)\\)

求出值\\(k\\)在下列无限矩阵中的第几行第几列,输出行号和列号。

1 2 5 10 ...
4 3 6 11 ...
9 8 7 12 ...
16 15 14 13 ...
... ... ... ... ...

首先考虑第\\(1\\)列,发现是完全平方数,\\((i,1)\\)中存储的值是\\(i^2\\)且反\\(L\\)型的边长为\\(i\\)

\\(i\\)个反\\(L\\)型中包含的数字有\\([(i-1)^2+1,i^2]\\),因此\\(k\\)在第\\(\\lceil \\sqrt{k}\\rceil\\)个反\\(L\\)型中。

接下来考虑\\(k\\)在反\\(L\\)型中是行号为\\(\\lceil \\sqrt{k}\\rceil\\)还是列号为\\(\\lceil \\sqrt{k}\\rceil\\)

观察\\((i,i)\\)上的数,其值为\\(i^2-i+1\\),因此

  • \\(k \\leq \\lceil \\sqrt{k}\\rceil^2 -\\lceil \\sqrt{k}\\rceil + 1\\),则列号为\\(\\lceil \\sqrt{k}\\rceil\\)可以求出行号\\(k-(\\lceil \\sqrt{k}\\rceil -1)^2\\)

    答案是\\((k-(\\lceil \\sqrt{k}\\rceil -1)^2,\\lceil \\sqrt{k}\\rceil)\\)

  • \\(k \\geq \\lceil \\sqrt{k}\\rceil^2 -\\lceil \\sqrt{k}\\rceil + 1\\),则行号为\\(\\lceil \\sqrt{k}\\rceil\\)可以求出列号\\(\\lceil \\sqrt{k}\\rceil ^2 - k + 1\\)

    答案是\\((\\lceil \\sqrt{k}\\rceil,\\lceil \\sqrt{k}\\rceil ^2 - k + 1)\\)

# include <bits/stdc++.h>
using namespace std;
void solve() {
	int k; scanf("%d",&k);
	int s = ceil(sqrt(k));
	if (k<=s*s-s+1) {
		printf("%d %d\\n",k-(s-1)*(s-1),s);
	} else {
		printf("%d %d\\n",s,s*s-k+1);
	}
}
signed main()
{
	int t; scanf("%d",&t);
	while (t--) {
		solve();
	}
	return 0;
}

D - Make a Power of Two

\\(t(1\\leq t \\leq 10^4)\\)组数据,每组数据给出一个数\\(n(1\\leq n\\leq 10^9)\\)

求最小的操作次数,让\\(n\' = 2^k (k \\in N)\\)

  • 删除数\\(n\\)中的一个数位。
  • 在数\\(n\\)尾部添加任意一个数字。

在数据范围内,有\\(2^0,2^1,...,2^{62}\\)\\(63\\)\\(2\\)的幂次,

对于\\(n\\),变成每一个\\(2\\)的幂次都计算一遍最下操作次数,再取最小值,就是最终答案了。

下面考虑\\(n\\)\\(s\\),求最小操作次数,让\\(n\\)变成\\(s\\)

首先应该在\\(n\\)中删除一些数字,删除最少的情况就是保留最多的情况。

\\(n\\)中的子序列,和\\(s\\)中从\\(1\\)开始的连续子串相等并且长度最大的情况,记此长度为\\(r\\)

那么在\\(n\\)中应该删除\\(|n| - r\\)个元素形成\\(n\'\\),在\\(n\'\\)中添加\\(|s|-r\\)个元素形成\\(s\\)

所以,答案就是\\(|n|+|s| - 2r\\)

\\(r\\)的计算用双指针可以轻松解决。

时间复杂度为\\(O(63\\times t\\times (|n|+|s|))\\)

# include<bits/stdc++.h>
# define int long long
# define pow hhh
# define inf (1e9)
using namespace std;
int pow[105],a[105],b[105],f[105][105];
int work(int x,int y) {
	a[0]=b[0]=0;
	while (x) { a[++a[0]]=x%10; x/=10;}
	while (y) { b[++b[0]]=y%10; y/=10;}
	reverse(a+1,a+1+a[0]); reverse(b+1,b+1+b[0]);
	int i=1,j=1;
	while (i<=a[0]&&j<=b[0]) {
		if (a[i]==b[j]) i++,j++;
		else i++;
	}
	int res=j-1;
	return a[0]+b[0]-2*res;
}
signed main() {
	int t; scanf("%lld",&t);
	pow[0]=1;
	for (int i=1;i<63;i++) pow[i]=pow[i-1]*2;
	while (t--) {
		int n; scanf("%lld",&n);
		int ans = 1e9;
		for (int i=0;i<63;i++) {
			ans=min(ans,work(n,pow[i]));
		}
		printf("%lld\\n",ans);
	}
	return 0;
} 

E - Polycarp and String Transformation

\\(T\\)组数据,\\(1 \\leq T \\leq 10^4\\)

初始有字符串\\(s\\),\\(t\\)为空。连续依次操作,\\(t +=s\\),\\(s\\)删除字符\\(c\\),前提是\\(c \\in s_i\\)直到\\(s = ""\\)

给出若干次操作后的结果\\(t\\),求出原字符串\\(s\\),和依次删除字符的顺序\\(c\\)

保证所有输入字符的总数不会超过\\(5\\times 10^5\\)

显然,\\(t\\)串倒序出现字母种类的顺序就是\\(s\\)串删除字母的顺序。

这是基于若当前删除一个字母,那么这个串后面的拼接到\\(t\\)的串上都不会出现这个字母了。

因此,很容易的就获得了,依次删除字符的顺序。

\\(t\\)的长度为\\(n\\),有\\(m\\)种不同的字符,设\\(cnt[c]\\)表示字符\\(c\\)\\(t\\)字符串中总共出现的次数,

那么初始的\\(s\\)经过且只经过\\(m\\)次操作,

基于每一次只删除一个字符的原理,我们可以计算出一个字符在原字符串\\(s\\)中出现的次数。

例如,最后一个被删除的字符\\(c_m\\),在原字符串\\(s\\)中出现的次数为\\(\\frac{cnt[c_m]}{m}\\)

倒数第\\(2\\)个被删除的字符\\(c_{m-1}\\),在原字符串\\(s\\)中出现的次数为\\(\\frac{cnt[c_{m-1}]}{m-1}\\)

因此\\(c_i (1\\leq i \\leq w)\\)在原字符串中出现的次数为\\(\\frac{cnt[c_i]}{i}\\)次,那么原字符串长度为\\(|s| = \\sum_{i=1}^{m} \\frac{cnt[c_i]}{i}\\)

这样,从\\(t[1]\\)开始往后长度为\\(|s|\\) 的字符串,是唯一有可能满足情况的答案。

返过去模拟验证,可以排除\\(-1\\)的情况,剩下的就是正确的答案。

时间复杂度\\(O(\\sum |t|)\\)

 include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int cnt[26];
int used[26];
char ans[N];
int main() {
	int t; scanf("%d",&t);
	while (t--) {
		string s; cin>>s;
		int n=s.length(),tot=0;
		for (int i=0;i<26;i++) {
			cnt[i]=0; used[i]=0;
		}
		for (int i=n-1;i>=0;i--) {
			if (!used[s[i]-\'a\']) used[s[i]-\'a\']=++tot;
			cnt[s[i]-\'a\']++;
		}
		for (int i=0;i<26;i++) if (used[i]) {
			ans[used[i]]=i+\'a\';
		}
		int res = 0; 
		for (int i=1;i<=tot;i++) res+=cnt[ans[i]-\'a\']/(tot-i+1);
		reverse(ans+1,ans+1+tot);
		string s1 = "",s2 = "";
		for (int i=0;i<res;i++) s1+=s[i];
		int now=1;
		while (s1!="") {
			s2+=s1;
			string w="";
			for (int i=0;i<s1.length();i++) if (ans[now]!=s1[i]) {
				w+=s1[i];
			}
			s1=w;
			now++;
		}
		if (s2 == s) {
			for (int i=0;i<res;i++) printf("%c",s[i]); 
			putchar(\' \');
			for (int i=1;i<=tot;i++) printf("%c",ans[i]);
			puts("");
		} else puts("-1");
	}
	return 0;
} 

F - Nearest Beautiful Number

\\(t(1\\leq t \\leq 10^4)\\)组数据,每组数据给出\\(2\\)个数\\(n,k (1\\leq n\\leq 10^9,1 \\leq k\\leq 10)\\)

求出最小的\\(x\\geq n\\),满足\\(x\\)由最多\\(k\\)种数字构成。

方法一:动态规划。

\\(f[i][mask][0/1]\\)表示当前从最高位到最低位依次访问,当前到低\\(i\\)位,取数集合为\\(mask\\)\\(0\\)当前数前缀和目标前缀相等,\\(1\\)当前数前缀已经大于目标前缀了。

\\(g[i][mask][0/1]\\)表示当前从最高位到最低位依次访问,当前到低\\(i\\)位,取数集合为\\(mask\\)\\(0\\)当前数前缀和目标前缀相等,\\(1\\)当前数前缀已经大于目标前缀了。此时的最小前缀值。

初始值:\\(f[0][0][0] = 1 ,g[0][0][0] = 0\\) otherwise : \\(f[i][mask][0/1]=0,g[i][mask][0/1]=inf\\)

转移:考虑\\(i\\)\\(i+1\\)转移。

  • $f[i+1][mask|(1<<a[i+1])][0]=1; $ (条件:\\(f[i][mask][0] =1\\))同时更新\\(g\\)
  • $f[i+1][mask|(1<<j)][1]=1; $ (条件:\\(f[i][mask][0] =1\\)并且\\(a[i+1]+1\\leq j\\leq 9\\))同时更新\\(g\\)
  • \\(f[i+1][mask|(1<<j)][1]=1;\\) (条件:\\(f[i][mask][1] =1 , 0 \\leq j \\leq 9\\))同时更新\\(g\\)

答案为 \\(\\min \\limits_{1\\leq count(mask)\\leq k }\\ g[n][mask][0/1]\\)

时间复杂度为\\(O(t\\times 2^k\\times k\\times |n|)\\) 数量级为\\(10^9\\),会\\(TLE\\)

# include <bits/stdc++.h>
# define int long long
# define inf (1e18)
using namespace std;
bool f[10][1024][2];
int g[10][1024][2],a[15];
char s[15];
inline int read()
{
	int x=0;char c=getchar();
	while (c<\'0\'||c>\'9\') c=getchar();
	while (c>=\'0\'&&c<=\'9\') x=x*10+c-\'0\',c=getchar();
	return x;
}
void write(int x) {
	if (x>9) write(x/10);
	putchar(x%10+\'0\'); 
}
int count(int x) {
	int res=0;
	for (;x;x-=x&(-x)) res++;
	return res; 
}
signed main() {
	int t=read(); 
	while (t--) {
		scanf("%s",s+1); int k=read(),n=strlen(s+1);
		for (int i=1;i<=n;i++) a[i]=s[i]-\'0\'; 
		for (int i=1;i<=n;i++)
			for (int mask=0;mask<(1<<10);mask++)
				for (int j=0;j<=1;j++) {
					f[i][mask][j]=0;
					g[i][mask][j]=inf;
				}
		f[0][0][0]=1; g[0][0][0]=0;
		for (int i=0;i<n;i++)
			for (int mask=0;mask<(1<<10);mask++) {
				if (count(mask)>k) continue;
				if (f[i][mask][0]) {
					f[i+1][mask|(1<<a[i+1])][0]=1;
					g[i+1][mask|(1<<a[i+1])][0]=min(g[i+1][mask|(1<<a[i+1])][0],g[i][mask][0]*10+a[i+1]);
					for (int j=a[i+1]+1;j<10;j++) {
						f[i+1][mask|(1<<j)][1]=1;
						g[i+1][mask|(1<<j)][1]=min(g[i+1][mask|(1<<j)][1],g[i][mask][0]*10+j);
					}
				}
				if (f[i][mask][1]) {
					for (int j=0;j<10;j++) {
						f[i+1][mask|(1<<j)][1]=1;
						g[i+1][mask|(1<<j)][1]=min(g[i+1][mask|(1<<j)][1],g[i][mask][1]*10+j);
					}
				}
			}
		int ans = inf;
		for (int mask=0;mask<(1<<10);mask++) {
			if (count(mask)>k) continue;
			if (f[n][mask][0]) ans=min(ans,g[n][mask][0]);
			if (f[n][mask][1]) ans=min(ans,g[n][mask][1]);
		}
		write(ans); putchar(\'\\n\');	
	}
	return 0;
 } 

方法二:贪心。

考虑\\(O(2^k)\\)枚举,需要的元素总数,然后尝试用这些元素去填充大于等于\\(n\\)\\(x\\),然后取最小的\\(x\\)

若当前选择的数可以和\\(n\\)的前缀匹配,那么就按照\\(n\\)的前缀填写\\(x\\)的对应值,

否则,

  • 若当前位存在至少一个比\\(n\\)的当前位要大的数字,那么选取这些数字里最小的那个填充,剩余的用需要选择的元素中最小值填充。
  • 若当前位不存在至少一个比\\(n\\)的当前位要大的数字,那么选取最近的一位存在至少一个比\\(n\\)的对应位要大的数字,将该位改为这些数字里最小的那个填充,剩余的用需要选择的元素中最小值填充。

时间复杂度\\(O(t\\times 2^k\\times |n|)\\) 数量级为\\(10^8\\),不一定\\(TLE\\)

# include <bits/stdc++.h>
# define int long long
# define inf (1e18)
# define lowbit(x) (x&(-x))
using namespace std;
int a[10];
int count(int x) {
	int res=0;
	for (;x;x-=lowbit(x)) res++;
	return res;
}
signed  main()
{
	int t; scanf("%lld",&t);
	while (t--) {
		int n,k; scanf("%lld%lld",&n,&k); a[0]=0;
		while (n) { a[++a[0]]=n%10; n/=10;}
		reverse(a+1,a+1+a[0]);
		int ans = inf;
		for (int mask=1;mask<(1<<10);mask++)
		    if (count(mask)<=k) {
			int res=0;
			for (int i=1;i<=a[0];i++) {
				if (mask&(1<<a[i])) res=res*10+a[i];
				else {
					int pos=-1,mn,mx;
					for (int j=a[i]+1;j<10;j++) if (mask&(1<<j)) {
						pos=j; break;
					}
					for (int j=0;j<10;j++) if (mask&(1<<j)) {
						mn=j; break;
					}
					for (int j=9;j>=0;j--) if (mask&(1<<j)) {
						mx=j; break;
					}
					if (pos==-1) {
						int w=-1;
						for (int j=i;j>=1;j--) if (a[j]<mx) {
							w=j; break;
						}
						if (w==-1) {
							res=inf;  break;
						}
						int nn;
						for (int j=a[w]+1;j<10;j++) if (mask&(1<<j)) {
							nn=j; break;
						}
						res=0;
						for (int j=1;j<=w-1;j++) res=res*10+a[j];
						res=res*10+nn;
						for (int j=w+1;j<=a[0];j++) res=res*10+mn;
					} else {
						res=res*10+pos;
						for (int j=i+1;j<=a[0];j++) res=res*10+mn;
					}
					break;
				}
			}
			ans=min(ans,res);
		}
		printf("%lld\\n",ans);
	}
	return 0;
 } 

以上是关于Codeforces Round #739 (Div. 3)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #739 (Div. 3) 题解

Codeforces Round #739 (Div. 3)A-F2

Codeforces Round #739 (Div. 3) ABCDEF1F2题解

Codeforces Round #739 (Div. 3) ABCDEF1F2题解

Codeforces Round #739 (Div. 3)(补题)

Codeforces Round #739 (Div. 3) F1. Nearest Beautiful Number (easy version)