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;
}
}