• Codeforces Round #708 (Div. 2)题解A,B,C1,C2,E1,E2


    [前言]md unrated掉气死了

    比赛链接:Codeforces Round #708 (Div. 2)
    官方题解:Tutorial

    A.Meximization

    题意:给一个n元素数组,让你重新排序,求所有前缀的最大Mex和。
    题解:如果一个前缀的前缀Mex=x,那他自己Mex>=x,可以知道所有大的Mex越早出现越好,因此是0,1,2,3,...,后面的随意。

    #include<bits/stdc++.h>
    
    using namespace std;
    #define f(i,a,b) for(register int i=a;i<=b;++i)
    #define ll long long
    #define N 2005
    
    inline int read(){int x;scanf("%d",&x);return x;}
    int a[N],num[105];
    void solve()
    {
    	memset(num,0,sizeof num);
    	int n=read();
    	f(i,1,n) num[read()]++;
    	int Max=0;
    	f(i,0,103) if(!num[i]) {
    		Max=i;
    		break;
    	}
    	f(i,0,Max-1) cout<<i<<" ",num[i]--;
    	f(i,0,100) 
    	{
    		while(num[i]) num[i]--,cout<<i<<" ";
    	}
    	cout<<endl;
    }
    int main()
    {
    	int T=read();
    	while(T--) solve();
    	return 0;
    }
    

    B.M-arrays

    题意:给一个n数字数组,和一个m值,要求将这个数组划分成最少的子数组,使之每个子数组满足任意两个相邻元素之和为m的倍数(对于只有一个元素的子数组默认满足)。
    题解:相邻两个元素之和为m倍数等价于相邻两个元素模m和为m,将所有的元素按照模m的值分组,所有模m的值为x的组和模m值为m-x的组配对,这样一一配对,就可以得到结果。
    不过要考虑三个细节:
    1:模m的值为0的数作为一组
    2:模m的值等于m/2的数为一组
    3:如果模m的值为x,m-x的两组数数量分别为s1,s2,则max(s1,s2)-min(s1-s2)必须小于等于1,多出来的几个数单个数字形成组
    考虑完这些,问题就简单了。

    #include<bits/stdc++.h>
    
    using namespace std;
    #define f(i,a,b) for(register int i=a;i<=b;++i)
    #define ll long long
    #define N 100005
    
    inline int read(){int x;scanf("%d",&x);return x;}
    int a[N],cnt[N];
    void solve()
    {
    	int n=read(),m=read(),ans=0;
    	f(i,0,m) cnt[i]=0;
    	f(i,1,n) 
    	{
    		a[i]=read();
    		if(a[i]%m==0) 
    		{
    			ans=1;
    			continue;
    		}
    		cnt[a[i]%m]++;
    	}
    	f(i,1,m-1)
    	{
    		if(!cnt[i]) continue;
    		if(i*2==m)
    		{
    			ans++;
    			cnt[i]=0;
    			continue;
    		}
    		int d=m-i;
    		if(cnt[d]>cnt[i]) swap(cnt[d],cnt[i]);
    		if(cnt[i]-cnt[d]<=1)
    		{
    			ans+=1;
    		}
    		else 
    		{
    			cnt[i]-=(cnt[d]+1);
    			ans+=cnt[i]+1;
    		}
    		cnt[d]=0;
    		cnt[i]=0;
    	}
    	cout<<ans<<endl;
    }
    int main()
    {
    	int T=read();
    	while(T--) solve();
    	return 0;
    }
    

    C1.k-LCM (easy version)

    题意:给你一个数n,让你求三个数x,y,z满足x+y+z=n且lcm(x,y,z)<=n/2
    题解:分类讨论:
    1:当n为奇数,分解成1,(n-1)/2,(n-1)/2 -> lcm(1,(n-1)/2,(n-1)/2)= (n-1)/2 <=n/2
    2:当n为偶数且(n-2)%4=0,分解成2,(n-2)/2,(n-2)/2,由于(n-2)%4=0,则(n-2)/2%2=0 -> lcm(2,(n-2)/2,(n-2)/2)= (n-2)/2 <=n/2
    3:如果不满足以上两种,则一定满足n%4=0,分解成n/2,n/4,n/4 -> lcm(n/2,n/4,n/4)=n/2

    #include<bits/stdc++.h>
    
    using namespace std;
    #define f(i,a,b) for(register int i=a;i<=b;++i)
    #define ll long long
    #define N 100005
    
    inline int read(){int x;scanf("%d",&x);return x;}
    int a[N],cnt[N];
    void solve()
    {
    	int n=read(),k=read();
    	if(n&1) cout<<1<<" "<<n/2<<" "<<n/2<<endl;
    	else
    	{
    		if((n-2)%4==0) cout<<2<<" "<<(n-2)/2<<" "<<(n-2)/2<<endl;
    		else cout<<n/2<<" "<<n/4<<" "<<n/4<<endl;
    	}
    }
    int main()
    {
    	int T=read();
    	while(T--) solve();
    	return 0;
    }
    

    C2.k-LCM (hard version)

    题意:给你数n和k,让你求k个数(k>=3),满足(a_1 + a_2 + a_3 + ... + a_k = n)(lcm(a_1,a_2,a_3,...,a_k)<=n/2)
    题解:更简单了,n = (k-3)+(n-k+3) ,k = (k-3)+3,其中k-3代表k-3个1,剩下的转化成C1题就行了。

    #include<bits/stdc++.h>
    
    using namespace std;
    #define f(i,a,b) for(register int i=a;i<=b;++i)
    #define ll long long
    #define N 100005
    
    inline int read(){int x;scanf("%d",&x);return x;}
    int a[N],cnt[N];
    void solve()
    {
    	int n=read(),k=read(),s=0;
    	s=k-3;
    	n=n-s;
    	f(i,1,s) cout<<1<<" ";
    	if(n&1) cout<<1<<" "<<n/2<<" "<<n/2<<endl;
    	else
    	{
    		if((n-2)%4==0) cout<<2<<" "<<(n-2)/2<<" "<<(n-2)/2<<endl;
    		else cout<<n/2<<" "<<n/4<<" "<<n/4<<endl;
    	}
    }
    int main()
    {
    	int T=read();
    	while(T--) solve();
    	return 0;
    }
    

    E1.Square-free division (easy version)

    题意:给一个n元素数组,(a[i]<1e7),求最少能把数组分成几段使得每段满足任意两个数的积不是平方数。
    题解:将每个数进行质因数分解,得到(x={a_1}^{b_1}*{a_2}^{b_2}*{a_3}^{b_3}*...*{a_k}^{b_k}),(y={a_1}^{c_1}*{a_2}^{c_2}*{a_3}^{c_3}*...*{a_k}^{c_k}),相乘得到(x*y={a_1}^{b_1+c_1}*{a_2}^{b_2+c_2}*{a_3}^{b_3+c_3}*...*{a_k}^{b_k+c_3}),xy是平方数的条件是所有相同的质因子的次数的奇偶性相同,这样就有一个处理的技巧,对于x,将其所有次数为奇数的质因子相乘(只乘一次),得到的数成为mask,若mask[x]=mask[y],则xy为平方数,所以将a数组直接用其mask代替,问题就转化成了:
    将a数组划分成最少的子数组使每个子数组不存在相同的数
    这样问题就简单很多了,从头到尾贪心一遍就行了。

    #include<bits/stdc++.h>
    
    using namespace std;
    #define f(i,a,b) for(register int i=a;i<=b;++i)
    #define ll long long
    #define N 100005
    
    inline int read(){int x;scanf("%d",&x);return x;}
    map<int,int>mp;
    int Divide(int x)
    {
    	int ans=1;
    	for(int i=2;i<=sqrt(x);++i)
    	{
    		if(x%i==0)
    		{
    			int s=0;
    			while(x%i==0) s++,x/=i;
    			if(s&1) ans=ans*i;
    		}
    	}
    	if(x!=1) ans=ans*x;
    	return ans;
    }
    void solve()
    {
    	mp.clear();
    	int n=read(),k=read(),ans=0;
    	for(int i=1;i<=n;++i)
    	{
    		int sum=Divide(read());
    		if(mp.count(sum)) 
    		{
    			ans++;
    			mp.clear();
    		}
    		mp[sum]=1;
    	}
    	cout<<ans+1<<endl;
    }
    int main()
    {
    	int T=read();
    	while(T--) solve();
    	return 0;
    }
    

    E2.Square-free division (hard version)

    题意:这题和上一题一样,但是增加了一个条件,你最多可以改变其中的k个数。
    题解:按照上题的思路,直接将每个数变成mask,问题就成了:
    在可以改变k个数的条件下,将a数组划分成最少的子数组使每个子数组不存在相同的数
    但是这样还是感觉不清楚,改变k个数,那我们只要改成原数组都不存在的数,那问题不就成了:
    在最多可以删除k个数的条件下,将a数组划分成最少的子数组使每个子数组不存在相同的数
    这样问题就突然似曾相识了,k<20,n<2e5,一下只就想到了二维dp,另dp[i][j]代表到第i个元素,删数j个,最少可以划分成几个子数组。
    首先预处理一个lef[i][j]数组,代表最小的下标使得删除j个的条件下可以将[lef[i][j],i]划分成一组,意思就是在[lef[i][j],i]这个区间有最多j个数多余,去掉这些多余的数,这个区间中的每个数就最多出现一次了。
    先预处理lef数组,可以在O(nk)的时间内处理出来(见下面的代码,cnt[i]代表[L,i]区间数字i出现的次数,sum代表几个数多余)

    for(int i=0;i<=k;++i)
    	{
    		int L=1,sum=0;
    		for(int j=1;j<=n;++j)
    		{
    			cnt[a[j]]++;
    			if(cnt[a[j]]>1) sum++;
    			while(sum>i)
    			{
    				cnt[a[L]]--;
    				if(cnt[a[L]]>=1) sum--;
    				L++;
    			}
    			lef[j][i]=L;
    		}
    		while(L<=n) cnt[a[L]]--,++L;
    	}
    

    lef数组预处理完后,这个问题就比较简单了,直接dp,状态转移:
    dp[i][j]=min(dp[i][j],dp[lef[i][q]][j-q]+1); (q<=j)
    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    #define f(i,a,b) for(register int i=a;i<=b;++i)
    #define ll long long
    #define N 200005
    inline int read(){int x;scanf("%d",&x);return x;}
    #define maxn 10000007
    int a[N],mask[maxn];
    int Divide(int x)
    {
    	if(mask[x]) return mask[x];
    	int ans=1,c=x;
    	for(int i=2;i<=sqrt(x);++i)
    	{
    		if(x%i==0)
    		{
    			int s=0;
    			while(x%i==0) s++,x/=i;
    			if(s&1) ans*=i;
    		}
    	}
    	if(x!=1) ans*=x;
    	mask[c]=ans;
    	return ans;
    }
    
    int dp[N][22],lef[N][22],cnt[maxn];
    void solve() 
    {
    	int n=read(),k=read();
    	f(i,1,n) a[i]=Divide(read());
    	for(int i=0;i<=k;++i)
    	{
    		int L=1,sum=0;
    		for(int j=1;j<=n;++j)
    		{
    			cnt[a[j]]++;
    			if(cnt[a[j]]>1) sum++;
    			while(sum>i)
    			{
    				cnt[a[L]]--;
    				if(cnt[a[L]]>=1) sum--;
    				L++;
    			}
    			lef[j][i]=L;
    		}
    		while(L<=n) cnt[a[L]]--,++L;
    		//f(j,1,n) cnt[a[j]]=0;
    	}
    	//f(i,0,n) f(j,0,k) dp[i][j]=99999999;
    	//dp[0][0]=0;
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<=k;++j)
    		{
    			dp[i][j]=2*n;
    			for(int q=0;q<=j;++q)
    				dp[i][j]=min(dp[i][j],dp[lef[i][q]-1][j-q]+1);
    		}
    	int ans=n*2;
    	f(i,0,k) ans=min(ans,dp[n][i]);	
    	printf("%d
    ",ans);
    }
    int main()
    {
    	int T=read();
    	while(T--) solve();
    	return 0;
    }
    
  • 相关阅读:
    03-三维空间刚体运动-基础知识体系
    特征值和特征向量
    齐次坐标和一般坐标
    c++中 重载 覆盖 隐藏的区别 附加 mutable笔记
    快速学习理解网络协议4
    快速学习理解网络协议3
    快速学习理解网络协议2
    快速学习理解网络协议1
    c++的直接初始化与复制初始化 未完成!!!!!!!!!!!!
    高性能网络编程(二)
  • 原文地址:https://www.cnblogs.com/Tiork/p/14556162.html
Copyright © 2020-2023  润新知