Codeforces div2 round 708题解

由于现在水平有限,暂时只补2100分及以下的题,这里只列出了除了E.2和D的题目
1.Meximization
大致题意,对于给定的一个数组,将其打乱使得对于打乱后的数组,其所有情况对应的所有从1~i的前缀的数组元素没有覆盖到的自然数的最小值之和最大。
思路:直接hash,找到从0开始第一个没有出现过的数k,然后输出[0,k-1],同时再把剩下的元素输出即可,时间复杂度是O(100)

#include<iostream>
#include<cstring>
using namespace std;
const int N=110;
int cnt[N];
int a[N];
int main()
{
 int t;
 cin>>t;
 while(t--)
 {
 	memset(cnt,0,sizeof cnt);
 	int n;
 	scanf("%d",&n);
 	 for(int i=0;i<n;i++)
 	  {
 	  	  scanf("%d",&a[i]);
 	  	  cnt[a[i]]++;
    }
    int k=0;
    for(int i=0;i<=100;i++)
      if(!cnt[i])
      {
      	k=i;
      	break;
  	}
   for(int i=0;i<k;i++)
     {
     printf("%d ",i);
     cnt[i]--;
     }
   for(int i=0;i<n;i++)
    if(cnt[a[i]]){
     printf("%d ",a[i]);
     cnt[a[i]]--;
     }
    puts("");
 }
}

B. M-arrays

大致题意:给定一个数组,将这个数组分成几个部分(不用保持原序),使得分割后的几个部分内部之间任意相邻两数之和不能被m整除,求最小的部分的个数

思路:将所有数对m先取模,并用hash统计每个数出现的次数,然后从前往后遍历数组,找到m-a[i]的数出现的次数,由于当这两个次数的绝对值之和小于2的时候是可以组成为1个区间的,那么就只算一个区间,否则剩下来的元素就只能单独分在一个部分了所以应该用max(cnt[a[i]],cnt[m-a[i]])-它们的最小值,然后求和即可
时间复杂度O(N);

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;;
int cnt[N];
bool st[N];
int main()
{
   int t;
   cin>>t;
   while(t--)
   {
   	memset(cnt,0,sizeof cnt);
   	memset(st,0,sizeof st);
   	int n,m;
   	scanf("%d%d",&n,&m);
   	for(int i=0;i<n;i++)
   	{
   		int k;
   		scanf("%d",&k);
   		cnt[k%m]++;
   	}
   	int res=0;
   	if(cnt[0]) res++;
   	for(int i=1;i<=m;i++)
   	 if(cnt[i]&&!st[m-i])
   	 {
   	 	int k=cnt[m-i];
   	 	if(k==0) {
   	 	res+=cnt[i];
   	 	//cnt[i]=0;
   	   }
   	 
   	    else{
   	      if(abs(cnt[i]-k)<=1)
   	      {
   	      	res++;
   	      	//cnt[i]=0;
   		  }
   		  else{
   		  	int mi=min(cnt[i],k);
   		  	res+=max(cnt[i],k)-mi;
   		  	//cnt[i]=0;
   		  }
   		}
   		st[i]=true;
   	}
   		printf("%d\n",res);
   }
}

C1. k-LCM (easy version)
大致题意,给定一个n,要求输出三个数,必须保证这三个数的和为n,且它们的最大公约数小于等于n/2

思路:构造
然后考虑n为奇数的情况,此时我们只需要分配两个n/2和1个1即可
然后n为偶数的情况
再看n/2是否为偶数,如果是偶数,那么只需要分配n/4,n/4,n/2
如果n/2为奇数,那么就分配,n/2,1,n-n/2-1.

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;;
int cnt[N];
bool st[N];
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n,k;
		scanf("%d%d",&n,&k);
		if(!(n%3))
		{
			printf("%d %d %d\n",n/3,n/3,n/3);
		} 
		else
		{
		   if(n%2)
		   {
		   	  printf("%d %d %d\n",n/2,n/2,1);
		   }
		   else{
		   	  int a=n/2;
               if(a%2)
			   {
			   	 printf("%d %d %d\n",a-1,a-1,2);
			   } 
               else
		   	  	printf("%d %d %d\n",a/2,a,a/2);
			  
		   }
		}
		
	}
}

C2. k-LCM (hard version)
在上题的基础上,对于输入的k,要输出k个数,使得满足上述情况

同理也是构造,我们只要尽可能多的给出1的数量即可,已知对于任何情况,我们总能构造出三个数的情况,那么我们对于k-3个数来说全选1,对其他3个数,只需要让它们构造出n-k+3即可

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n,k;
		scanf("%d%d",&n,&k);
		for(int i=0;i<k-3;i++)
		 printf("1 ");
		int m=n-k+3;
		//cout<<endl<<m<<endl;
		if(m%3==0)
		{
			printf("%d %d %d\n",m/3,m/3,m/3);
		}
		else{
			if(m%2)
		     {
		     	//cout<<415641<<endl;
		  	printf("%d %d 1\n",m/2,m/2);
			 }
			 else{
			 	int mt=m/2;
			 	if(mt%2)
			 	{
			 		printf("%d %d 2\n",mt-1,mt-1);
				}
				else printf("%d %d %d\n",mt/2,mt,mt/2);
			 }
		}
	} 
}

E1. Square-free division (easy version)
大致题意:给定一个数组,要求将数组分块(保持原序),使得每一个块内任意两个元素的乘积不是完全平方,求最小块数(单个元素一个块也可以)

思路:根据算数基本定理,任何一个数都可以写成素数的次方之积,要想使得两个数的乘积不是完全平方,那么就要使得对于所有除去了偶数次的质因子之后剩下的质因子不相等即可.那么只需要从前往后遍历,然后用hash记录之前保存的质因子,如果当前质因子之前已经出现过,那么就新开一个块。
而质因子则用线性筛法打表出前1e4中的所有素数,然后再遍历即可,因为对一个n来说,小于等于它的质数大概有log n个,因此可以极大优化,如果用普通的筛掉满足它因子的方法,大概是根号N次,可能会超时,用质因子筛可以优化到logn

时间复杂度:O(1e4+nlogn)

#include<iostream>
#include<cstring>
#include<unordered_map>
#include<cmath>
using namespace std;
unordered_map<int,bool> m;
const int N=2e5+10;
const int M=1e4;
int a[N];
bool st[M+10];
int primes[M],cnt;
void getprime()
{
  for(int i=2;i<=M;i++)
  {
  	if(!st[i])
  	{
  		primes[cnt++]=i;
  	}
  	for(int j=0;primes[j]<=M/i;j++)
  	{
  		st[i*primes[j]]=true;
  		if(i%primes[j]==0) break;
  	}
  }
}
int main()
{
  int t;
  cin>>t;
  getprime();
  /*for(int i=0;i<cnt;i++)
  printf("%d ",primes[i]);*/
  //cout<<cnt;
  
  while(t--)
  {
  	m.clear();
  	int n,k;
  	scanf("%d%d",&n,&k);
  	int res=1;
  	for(int i=1;i<=n;i++)
  	 scanf("%d",&a[i]);
  	for(int i=1;i<=n;i++)
  	{
  		for(int j=0;j<cnt&&primes[j]*primes[j]<=a[i];j++)
  		 {	 				 	
  		 	int k=primes[j]*primes[j];
  		 	if(k>a[i]) break;
  		 	while(!(a[i]%k))
  		 	{
  		 	   a[i]/=k;
  			}
  		 }
  	}
  	/*for(int i=1;i<=n;i++)
  	 printf("%d ",a[i]); 
  	 puts("");*/
  	for(int i=1;i<=n;i++)
  	{
  	    if(!m.count(a[i]))
  	    {
  	    	m[a[i]]=true;
  		}
  		else{
  			res++;
  			m.clear();
  			m[a[i]]=true;
  		}
  	}
  	printf("%d\n",res); 
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值