VP记录:Codeforces Round 868 (Div. 2) A~D

传送门:CF

A题:A-characteristic

构造一个只有 1 , − 1 1,-1 1,1的数组,满足乘积为 1 1 1的数对的个数为 k k k.
发现 n n n的范围很小,考虑直接暴力枚举数组中 1 1 1的个数,记为 i i i,那么对于1的所有数对来说,我们有 i ∗ ( i − 1 ) / 2 i*(i-1)/2 i(i1)/2个,然后对于 − 1 -1 1的个数也不难表示出为 n − i n-i ni,然后此时的贡献为 ( n − i − 1 ) ∗ ( n − i ) / 2 (n-i-1)*(n-i)/2 (ni1)(ni)/2,判断即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int main() {
	int T=read();
	while(T--) {
		int n=read(),k=read();
		int ans1=-1,ans2=-1;
		for(int i=1;i<=n;i++) {
			int j=n-i;
			if((i-1)*i/2+(j-1)*j/2==k) {
				ans1=i;ans2=j;
				break;
			}
		}
		if(ans1==-1) {
			cout<<"NO"<<endl;
		}
		else {
			cout<<"YES"<<endl;
			for(int i=1;i<=ans1;i++) {
				cout<<"1 ";
			}
			for(int i=1;i<=ans2;i++) {
				cout<<"-1 ";
			}
			cout<<endl;
		}
	}
	return 0;
}

B题:Sort with Step

因为我们只能调换相距为 k k k的数对,那么我们可以将这个数组分为几个轨道,每一个轨道上的数字可以进行交换,不在同一个轨道上的数字不能进行交换.那么假设我们需要进行交换,也肯定是交换不在自己应该在的轨道上的那两个数字.所以我们可以直接记录不在自己应该在的轨道上的数字的个数(记为 k k k).因为我们唯一能使最后序列满足的情况就是将所有数字换到他们应该在的地方.所以当 k k k等于 1 1 1的时候代表其不需要进行初步交换,反之则需要进行 k k k次交换.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];
int main() {
	int T=read();
	while(T--) {
		int n=read(),k=read();
		for(int i=1;i<=n;i++) {
			a[i]=read();
		}
		int cnt=0;int pos=-1;
		for(int i=1;i<=n;i++) {
			if(abs(i-a[i])%k!=0) {
				cnt++;
			}
		}
		if(cnt==0) {
			cout<<0<<endl;
		}
		else {
			if(cnt==2) {
				cout<<1<<endl;
			}
			else {
				cout<<-1<<endl;
			}
		}
	}
	return 0;
}

C题:Strongly Composite

一道需要进行分解质因数的题目.首先我们发现b数组的乘积要和a数组一样.那么不妨将a数组中的乘积进行一个分解质因数,先预处理存起来.显然我们最终b数组的所有数字就是由这些质因数组合而成
然后我们考虑强复合的性质.我们贪心的去想,肯定是用最小个数的质因数来组成一个数字是最优的.所以此时我们有两种组合方式:
1.我们拿两个相同的数来组成一个数 b i bi bi,此时我们的 b i bi bi显然是一个平方数,因为其实由两个质数组合而成,所以此时 b i bi bi显然是一个强复合.
2.显然1方案是最优的方案(因为花费的数字最少).但是此方案需要相同的数字.在1的方案下我们用完了所有的相同的数字,那么此时我们剩下来的就是不同的数字了.我们此时会发现一个很有趣的性质,我们拿出三个质数(记为 i i i, j j j, k k k)乘起来显然是一个强复合.因为两个质数乘起来肯定是一个合数,然后三个质数乘起来显然也是一个合数.又因为质因数的性质,此时我们的因子就是 1 , i , j , k , i ∗ j , i ∗ k , j ∗ k , i ∗ j ∗ k 1,i,j,k,i*j,i*k,j*k,i*j*k 1,i,j,k,ij,ik,jk,ijk,发现恰好是一个强复合数

至此本题也就不难解决了.


本部分介绍一下质因数分解算法的证明,如果熟悉的可以跳过:

for(int j=2;j<=__builtin_sqrt(a[i]);j++) {
	while(temp%j==0) {
		mp[j]++;
		temp/=j;
	}
}		
if(temp!=1) mp[temp]++;

不难发现上述算法只判断到了 n \sqrt{n} n .下面证明一下这个算法的正确性.
首先证明这么做的得到的所有 j j j都是质数:
对于本部分采用反证法进行证明,假设我们当前枚举到的因子 j j j不是一个质数,那么他肯定是一个合数.此时根据唯一分解定理, j j j可以分解成 p 1 k 1 ∗ p 2 k 2 ∗ . . . ∗ p n k n p_1^{k_1}*p_2^{k_2}*...*p_n^{k_n} p1k1p2k2...pnkn这样的形式,那么必然存在 p i < j p_i<j pi<j,但是这样的因子我们在之前枚举到 p i p_i pi时的 w h i l e ( t e m p % j = = 0 ) while(temp\%j==0) while(temp%j==0)已经消掉了,所以不存在这种情况,所以 j j j必然是一个质数
接下来证明一下为什么只需要枚举到 n \sqrt{n} n ,也就是证明一下最后剩下来的那个数字要么是1要么是一个大于 n \sqrt{n} n 的质数
同样采取反证法进行证明,假设最后存在的那个数字k是一个大于 n \sqrt{n} n 的合数.那么根据之前的证明我们会发现这个合数必然是不存在小于 n \sqrt{n} n 的因子的.又因为他是一个合数,所以必然有两个大于 n \sqrt{n} n 的因子,但是此时两个因子相乘得到的数显然比 n n n大,这是不合理的.所以最后剩下来的那个数必然是一个质数
至此证毕


2023.6 update:由于该分解方法复杂度较大,但是 Pollard-rho 分解质因数方法又比较麻烦,所以建议将此方法结合素数筛一起使用,能够大大减少复杂度

void init(int limit=__builtin_sqrt(1e9)) {
	for(int i=2;i<=limit;i++) {
		if(vis[i]) continue;
		prime[++cnt]=i;
		for(int j=i+i;j<=limit;j+=i) {
			vis[j]=1;
		}
	}
}
for(int j=1;j<=cnt;j++) {
	if(temp%prime[j]==0) mp[prime[j]]++;
	while(temp%prime[j]==0) temp/=prime[j];
}
if(temp!=1) mp[temp]++;

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define int long long
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];map<int,int>mp;
map<int,int>mp2;
signed main() {
	int T=read();
	while(T--) {
		int n=read();mp.clear();mp2.clear();
		ll sum=1;int cnt=0;int ans=0;
		for(int i=1;i<=n;i++) {
			a[i]=read();int temp=a[i];
			if(a[i]==1) continue;
			for(int j=2;j<=__builtin_sqrt(a[i]);j++) {
				while(temp%j==0) {
					mp[j]++;
					temp/=j;
				}
			}		
			if(temp!=1) mp[temp]++;
		}
		for(auto it : mp) {
			ans+=it.second/2;
			mp[it.first]=it.second-it.second/2*2;
		}
		for(auto it : mp) {
			cnt+=it.second;
		}
		ans+=cnt/3;
		cout<<ans<<endl;
	}
	return 0;
}

D题:Unique Palindromes

首先我们需要知道这样的一个事实,也就是我们每一次在一个字符串的后缀中加上一个任意字符a,我们的回文子串的增加个数最大为1.
简单证明一下这个结论:
我们假设现在有一个字符串为 s s s,那么假设我们此时加上一个 a a a使其增加了回文子串的个数.因为子串是连续的,所以此时我们不妨设多出了两个回文子串 s 1 + a , s 2 + a s1+a,s2+a s1+a,s2+a,并且 s 2 s2 s2 s 1 s1 s1的后缀.不难发现 s 1 + a s1+a s1+a包含了 s 2 + a s2+a s2+a,又因为 s 1 + a s1+a s1+a是一个回文串,所以 s 2 + a s2+a s2+a既是 s 1 + a s1+a s1+a的后缀又是它的前缀.那么此时要么 s 2 + a = = s 1 + a s2+a==s1+a s2+a==s1+a,要么 s 2 + a s2+a s2+a是被 s 2 s2 s2包含的.因为当 s 2 + a s2+a s2+a的长度小于 s 1 + a s1+a s1+a时,又因为是前缀,显然它是被 s 2 s2 s2包含的, s 2 + a s2+a s2+a的长度又不可能大于 s 1 + a s1+a s1+a
至此证毕

所以显然当存在 c [ i ] − c [ i − 1 ] > x [ i ] − x [ i − 1 ] c[i]-c[i-1]>x[i]-x[i-1] c[i]c[i1]>x[i]x[i1]时我们是无法构造的.此处特判一下
然后我们考虑构造,因为ci>3.所以考虑将"abc"作为前缀,当我们此时的c1不够时不断的补上字符"c"来使得其满足.可能会存在回文子串够了但是长度不够的情况.所以我们此时需要补上后缀.此时我们可以反复的补上"abc".举个简单的栗子:
a b c c c    ∣    a b c a b c a abccc\;|\; abcabca abcccabcabca,此时我们会很神奇的发现这样就不会增加回文子串了.
然后我们遇到了后面的回文条件.为了不会和前面已有的字符形成回文子串,我们考虑使用新的字符即可,比如 d , e , f . . . d,e,f... d,e,f....我们发现k<20.所以我们的26个字符是够用的.每一次多增加一个字符我们就可以得到一个贡献,当我们的贡献满时继续使用"abc"填满即可.
注意此时有一个小细节,我们此时的填充需要继续上述的循环,不然可能出现下述情况:ccc    ∣    \;|\; abca    ∣    \;|\; dda,我们会发现会导致意外增加了一个回文子串.

光讲可能比较难以理解,建议结合代码食用

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int x[maxn],c[maxn];
int main() {
	int T=read();
	while(T--) {
		int n=read(),k=read();
		for(int i=1;i<=k;i++) x[i]=read();
		for(int i=1;i<=k;i++) c[i]=read();
		int flag=0;
		for(int i=1;i<=k;i++) {
			if(c[i]-c[i-1]>x[i]-x[i-1]) {
				flag=1;
				break;
			}
		}
		if(flag==1) {
			cout<<"NO"<<endl;
			continue;
		}
		vector<char>ans;char kkk='c';
		ans.push_back('a');ans.push_back('b');ans.push_back('c');
		int pos=4;int hhh=3;char loop='a';
		for(int i=1;i<=k;i++) {
			if(i==1) {
				while(pos<=x[i]) {
					if(pos<=c[i]) {
						ans.push_back('c');
					}
					else {
						ans.push_back(loop);
						loop++;
						if(loop=='d') loop='a';
					}
					pos++;
				}
			}
			else{
				while(pos<=x[i]) {
					if(x[i]-pos+1>c[i]-c[i-1]) {//先达成条件再补长度
						ans.push_back(loop);
						loop++;
						if(loop=='d') loop='a';
					}
					else {
						ans.push_back('c'+i-1);
					}
					pos++;
				}
			}
			
		}
		cout<<"YES"<<endl;
		for(int i=0;i<ans.size();i++) cout<<ans[i];
		cout<<endl;
	}
	return 0;	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值