Codeforces Round #523 (Div. 2) A B (暴力) C (欧拉函数变形+dp)

A. Coins

题目链接:https://codeforces.com/contest/1061/problem/A

题目大意:n种树,每种数可以不限量的取,问组成x最少需要多少个数

题解:直接判断能取最大的全取最大的,不能取最大的取一个补充的

int main()
{
	std::ios::sync_with_stdio(false);
	int n,s;
	while(cin>>n>>s)
	{
		if(s<=n)
			cout<<1<<endl;
		else if(s%n)
			cout<<s/n+1<<endl;
		else
			cout<<s/n<<endl;
	}
}

B. Views Matter

题目链接:https://codeforces.com/contest/1061/problem/B

题目大意:给你n堆箱子,问最多能撤掉多少箱子,使三视图保持不变,箱子可以悬空,也就是说撤掉箱子下面的箱子后,箱子还是在原位置(牛顿哭了...)。

题解:遍历一遍,一堆只放一个依次递增,然后看最后面的那个需要多少才能补齐,总数减去最少的箱子数量就是答案了


int arr[MAXN];

int main()
{
	std::ios::sync_with_stdio(false);
	int n,m;
	while(cin>>n>>m)
	{
		clean(arr,0);
		ll maxl=0,sum=0;
		for(int i=1;i<=n;++i)
		{
			cin>>arr[i];
			maxl=maxl>arr[i]?maxl:arr[i];
			sum+=arr[i];
		}
		sort(arr+1,arr+n+1);
		ll res=0,el=0;
		for(int i=1;i<=n;++i)
		{
			if(arr[i]>el)
			{
				el++;//���ϼ�һ�� 
			}
			res++;
		}
		if(maxl>el)
			res+=(maxl-el);
		cout<<sum-res<<endl;
	}
	
	
}

C. Multiplicity

题目链接:https://codeforces.com/contest/1061/problem/C

题目大意:给出n个数,然后判断其中的某些数,组合能不能构成好数组,好数组b的定义是:对于每个bi,满足bi%i==0

求出能构成多少个好数组,数组b中的元素的顺序只能是数组a中元素的顺序,例如  {22,14}只能是{22,14},而不是{14,22}。

题解:很明显,遍历必定超时,因为,后面加一位在前面的基础上加的,因此状态转移方程就是:

遍历到第i个元素时,

if(arr[i]%j==0)

    dp[i][j]=dp[i-1][j-1]+dp[i-1][j]

else

    dp[i][j]=dp[i-1][j]

由于i,j范围时1e5,所以二维的数组不行,要变成一维的。

我们观察状态转移方程可以发现,i这一维度只是提供了上一个状态的值,因此我们如果从后向前刷新的话就可以直接用前面的数了,也不用担心会造成影响。

然后是找每个数的约数,用欧拉函数的方法找到每个数的约数,预处理出来:intt(),注意处理出来约数之后要进行排序,因为我们是从后向前更新的,因此要有序才行;

然后就开始动态规划,注意一下取模就行了:2200ms+

//#pragma comment(linker, "/STACK:1024000000,1024000000") 
 
#include<stdio.h>
#include<string.h>  
#include<math.h>  
  
//#include<map>   
//#include<set>
#include<deque>  
#include<queue>  
#include<stack>  
#include<bitset> 
#include<string>  
#include<fstream>
#include<iostream>  
#include<algorithm>  
using namespace std;  
 
#define ll long long  
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b) 
#define clean(a,b) memset(a,b,sizeof(a))// 水印 
//std::ios::sync_with_stdio(false);
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
const ll mod=1e9+7;

int arr[MAXN];
int dp[MAXN];
vector<int> few[MAXN];

void intt()
{
	for(int i=1;i<MAXN;++i)
	{
		for(int j=1;j*j<=i;++j)
		{
			if(i%j==0)//符合条件 
			{
				if(j*j!=i)
					few[i].push_back(i/j);
				few[i].push_back(j);
			}
		}
	}
	for(int i=1;i<MAXN;++i)
		sort(few[i].begin(),few[i].end());
}

int main()
{
	std::ios::sync_with_stdio(false);
	intt();
	int n;
	while(cin>>n)
	{
		clean(dp,0);
		clean(arr,0);
		for(int i=1;i<=n;++i)
			cin>>arr[i];
		dp[0]=dp[1]=1;//第一个元素是1且只能存在第一位 
		for(int i=2;i<=n;++i)//从第二个元素开始找 
		{
			for(int j=few[arr[i]].size()-1;j>=0;--j)//遍历所有的因子 
				dp[few[arr[i]][j]]=(dp[few[arr[i]][j]]+dp[few[arr[i]][j]-1])%mod;
		}
		ll ans=0;
		for(int i=1;i<=n;++i)
			ans=(ans+dp[i])%mod;
		cout<<ans<<endl;
	}
	
	
}

下面是大佬的220ms+,超优秀啊!学习一波:

//#pragma comment(linker, "/STACK:1024000000,1024000000") 
 
#include<stdio.h>
#include<string.h>  
#include<math.h>  
  
#include<map>   
//#include<set>
#include<deque>  
#include<queue>  
#include<stack>  
#include<bitset> 
#include<string>  
#include<fstream>
#include<iostream>  
#include<algorithm>  
using namespace std;  
 
#define ll long long  
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b) 
#define clean(a,b) memset(a,b,sizeof(a))// 水印 
//std::ios::sync_with_stdio(false);
const double PI=acos(-1.0);
const int MAXN=1e5+10;
const ll INF=1e16;
const ll mod=1e9+7;

int arr[MAXN];
int dp[MAXN*10];//dp[i]中存的是 长度为i的一串数有多少种组合 

int main()
{
	std::ios::sync_with_stdio(false);
	int n;
	while(cin>>n)
	{
		clean(dp,0);
		for(int i=1;i<=n;++i)
			cin>>arr[i];
		dp[0]=dp[1]=1;//数字至少有一个,长度为1的数组至少有一个 
		for(int i=2;i<=n;++i)//从第二个元素开始 
		{//遍历每个元素,找到他能够插入的位置,刷新dp 
			int sq=sqrt(arr[i]);
			//解释sqrt(arr[i]) 如果arr[i]要被整除,那么总共有t个因数,
			//这些因数就是能放的位置,因为里面可能会有一些重复的计数(参考欧拉函数),
			//所以我们只用存一半就行了,另一部分在循环的时候直接判断即可 
			for(int j=min(i,sq);j>=1;--j)//从最多能够插入的地方开始找,找到成为第一个元素 
			{//按照顺序,arr[i]元素最多能够插到 min(i个元素,sqrt(arr[i]))个元素后面 
				if(arr[i]%j==0)//如果符合要求 
				{//arr[i]可以成为第j号元素 
					// 这里相当于分为两个区间分别从后往前更新,避免重复利用新dp的值 
					if(j*j<arr[i]&&arr[i]/j<=i)//约数不重复 && 该位置<=最大插入位置 
						dp[arr[i]/j]=(dp[arr[i]/j]+dp[arr[i]/j-1])%mod;
					dp[j]=(dp[j]+dp[j-1])%mod;
				}
			}
		}
		ll ans=0;
		for(int i=1;i<=1e6;++i)
			ans=(ans+dp[i])%mod;
		cout<<ans<<endl;
	}
	
}

猜你喜欢

转载自blog.csdn.net/qq_40482358/article/details/84777787