1.拼不出的数(lost.in/.out/.cpp)
时间限制
1000ms
空间限制
256MB
【问题描述】
3 个元素的集合
{5,1,2}
的所有子集的和分别是
0,1,2,3,5,6,7,8
。发现最小的不能由该集合的子集拼出的数字是
4
。
现在给你一个
注意
32
位数字表示范围。
【输入格式】
第一行一个整数
n
。
第二行
【输出格式】
一行一个正整数表示答案。
【样例输入】
3
5 1 2
【样例输出】
4
【数据规模和约定】
对于
30%
的数据,满足
n≤15
对于
60%
的数据,满足
n≤1000
对于
100%
的数据,满足
n≤100000,1≤ai≤109
保证
ai
互不相同
solution
对于 30% 的数据
- 2n 暴力枚举所有子集,然后暴力找最小值。
对于 100% 的数据
把 a 数组排序。
考虑每插入一个数之后,可以得到的最大值,也就是插入之前的所有数的和
sum 然后插入一个数 x 之后,如果
x>sum+1 ,那就输出 sum+1这个为什么对呢,很显然
yinweiwoyebuzhidao
其实因为数据比较水,所以你只需要30分暴力,判断有没有1或2,剩下的输出所有数的和, 就可以A,亲测
code
- 因为数据水才AC的code
#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
template<typename T>
void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
#define MAXN 100010
map<ll,bool> g;
ll a[MAXN];
int main() {
int n;
input(n);
ll sum=0;
g[0]=true;
ll Min=1;
for(int i=0;i<n;i++) {
input(a[i]);
g[a[i]]=true;
if(g[Min]==true) Min++;
sum+=a[i];
}
if(g[1]==false) {
printf("1");
return 0;
}
if(g[2]==false) {
printf("2");
return 0;
}
if(n<=15) {
ll cnt;
for(int s=0;s<1<<n;s++) {
cnt=0;
for(int i=0;i<n;i++)
if(s&(1<<i))
cnt+=a[i];
g[cnt]=true;
}
while(g[Min]==true) Min++;
printf("%lld",Min);
return 0;
}
printf("%lld",sum+1);
return 0;
}
- 不知道能不能过比较强的数据的std
据出题人说可以卡
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int a[100005];
int main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
ll sum=0;
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) {
if(a[i]>sum+1) {
printf("%lld\n",sum+1);
return 0;
} else sum+=a[i];
}
printf("%lld\n",sum+1);
return 0;
}
2.整除(div.in/.out/.cpp)
时间限制
1000ms
空间限制
256MB
【问题描述】
给定整数
n
,问
如
注意
【输入格式】
一行一个整数 n 。
【输出格式】
一个整数表示答案。
【样例输入】
5
【样例输出】
3
【数据规模和约定】
对于
对于
60%
的数据,满足
1≤n≤1012
对于
100%
的数据,满足
1≤n≤1018
solution
对于 30% 的数据,直接暴力枚举判断就好了。
对于 60% 的数据
- ⌊ni⌋ 的值有一段区间是完全相等的,然后就可以分块来求,也就是除法分块,统计区间个数就是最后的答案。
对于 100% 的数据
利用上面随便一种方法打一个表找一下规律
如果用 f[n] 来表示 ⌊ni⌋ 不同的值的个数
通过打表可以发现这个 f[n] 也是有一段区间是相同的,所以再把转折点打一个表
打出来就是 1,2,4,6,9,12,16,20,25,30,36⋯
然后就发现其中一些数是完全平方数,但中间还夹着一个数,把完全平方数展开
数列变成 1∗1,2,2∗2,6,3∗3,12,4∗4,20,5∗5,30,6∗6
然后就很显然啦,中间夹着的数就是与他相邻的完全平方数的平方根的乘积
也就是 1∗1,1∗2,2∗2,2∗3,3∗3,3∗4,4∗4,4∗5,5∗5,5∗6,6∗6
找到这个规律以后再看一下它们和答案的关系
对应的答案依次是 1,2,3,4,5,6,7,8,9,10,11
把完全平方数对应的答案提出来
就是 f[1]=1,f[4]=3,f[9]=5,f[16]=7,f[25]=9,f[36]=11
规律显然是 2∗n√−1
然后就得到了这个题目 O(1) 的做法
找出一个数离他最近的两个完全平方数,小的那个为 k1 ,大的那个为 k2 ,中间的那个点就是 k3=k1−−√∗k2−−√
如果 k1≤n<k3 ,就输出 2∗k1−−√−1
如果 k3≤n<k2 ,就输出 2∗k1−−√
否则输出 2∗k2−−√
然后问题就是怎么找最近的两个完全平方数
其实这个很简单
k1≤n≤k2⇒k1−−√≤n√≤k2−−√ ,因为 k1 和 k2 是两个相邻的完全平方数,所以 k1+1=k2 ,所以 n√ 肯定是两个相差为 1 的数之间的数
所以
⌊n√⌋=k1−−√ , ⌈n√⌉=k2−−√一开始没想到这样,naive的我想把所有的完全平方数打个表,结果打了
14.2GB
还没打完,直接打满C盘,蓝屏is wonderful。还好最后想出来了。
code
- O(1) 虐std的code
#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
template<typename T>
void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
int main() {
freopen("div.in","r",stdin);
freopen("div.out","w",stdout);
ll n;
input(n);
ll k1=floor(sqrt(n)*1.00),k2=ceil(sqrt(n)*1.00),k3=k1*k2;
if(k1*k1<=n&&n<k3) cout<<2*k1-1;
else if(k3<=n&&n<k2*k2) cout<<2*k1;
else cout<<2*k2-1;
fclose(stdin);
fclose(stdout);
return 0;
}
3.钻石(diamond.in/.out/.cpp)
时间限制
1000ms
空间限制
256MB
【问题描述】
你有
n
个“量子态” 的盒子,每个盒子里可能是一些钱也可能是一个钻石。
现在你知道如果打开第
现在你想知道,自己恰好获得
k(0≤k≤n)
个钻石,并且获得钱数大于等于
m
的概率是多少。
请你对
答案四舍五入保留
3
位小数。
【输入格式】
第一行两个整数
接下来
n
行,每行两个整数
【输出格式】
输出共 n+1 行,表示 0≤k≤n 的答案。
【样例输入】
2 3
2 50
3 50
【样例输出】
0.250
0.250
0.000
【数据规模和约定】
对于
30%
的数据,
n≤10
对于
60%
的数据,
n≤15
对于
100%
的数据,
n≤30,1≤Pi≤99,1≤Vi≤107,1≤m≤107
solution
std的思路
主要思想是meet in the middle
暴力枚举每个物品选还是不选,是 2n ,明显不能A
但是我们可以搜两次,一次搜 2n ,那样复杂度就是 2∗2n/2 ,这个复杂度就可以A了
然后就是怎么搜了
首先要知道怎么枚举子集,我的做法是压成二进制串来做,关于这个推荐这篇blog
会了这个以后就好了, 每次搜 2n/2 ,最后合并就好了。
表示以上代码写不出来,看来还要提高码力。
所以我们来写dp吧
令 f[i][j] 表示前 i 个盒子获得
j 颗钻石的概率考虑第 i 个盒子拿不拿钻石
如果拿那就有
f[i][j]+=f[i−1][j−1]∗(100−p[i])/100.0 如果不拿那就有 f[i][j]+=f[i−1][j]∗p[i]/100.0
先这样跑一遍求出的是没有限制的时候的概率,然后再用dfs减去获得的钱不满足条件的概率
感觉dp比什么meet in the middle不知道好到哪里去
code
- 自己写的dp
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 40
int n,m;
double f[MAXN][MAXN];
int v[MAXN],p[MAXN];
void dfs(int cnt,double P,int money,int diamond){
if(money>=m)return;
if(cnt==n+1){
if(money<m)f[n][diamond]-=P;
return;
}
dfs(cnt+1,P*p[cnt]/100.0,money+v[cnt],diamond);
dfs(cnt+1,P*(100-p[cnt])/100.0,money,diamond+1);
return;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%d",&v[i],&p[i]);
f[1][0]=p[1]/100.0;
f[1][1]=(100-p[1])/100.0;
for(int i=2;i<=n;i++)
for(int j=0;j<=i;j++){
if(j) f[i][j]+=f[i-1][j-1]*(100-p[i])/100.0;
f[i][j]+=f[i-1][j]*(p[i]/100.0);
}
dfs(1,1,0,0);
for(int i=0;i<=n;i++)
printf("%.3lf\n",f[n][i]);
return 0;
}
- std的meet in the middle
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int tt;
int n,m;
int v[35];
double p[35];
double ans[35];
vector<pair<int,double> > sta[35];
int main(){
freopen("diamond.in","r",stdin);
freopen("diamond.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1,x;i<=n;i++){
scanf("%d%d",&v[i],&x);
p[i]=x/100.;
}
for(int i=0;i<=n;i++){
sta[i].clear();
}
int an=(n/2.5)+1;
int bn=n-an;
for(int st=0;st<1<<bn;st++){
double nowp=1;
int cnt=0,money=0;
for(int i=0;i<bn;i++){
if((st>>i)&1){
money+=v[n-i];
nowp*=p[n-i];
}else{
cnt++;
nowp*=(1-p[n-i]);
}
}
sta[cnt].push_back(make_pair(money,nowp));
}
for(int i=0;i<=n;i++){
sort(sta[i].begin(),sta[i].end());
for(int j=1;j<sta[i].size();j++){
sta[i][j].second+=sta[i][j-1].second;
}
}
for(int st=0;st<1<<an;st++){
double nowp=1;
int cnt=0,money=0;
for(int i=0;i<an;i++){
if((st>>i)&1){
money+=v[i+1];
nowp*=p[i+1];
}else{
cnt++;
nowp*=(1-p[i+1]);
}
}
for(int i=0;i<=bn;i++){
// now d =cnt+i
int L = m-money;
vector<pair<int,double> >::iterator it = lower_bound(sta[i].begin(),sta[i].end(),make_pair(L,-1.));
double tmp = sta[i].back().second;
if(it!= sta[i].begin()){
it--;
tmp-=it->second;
}
ans[cnt+i] += tmp*nowp;
}
}
for(int i=0;i<=n;i++){
printf("%.3f\n",ans[i]);
}
fclose(stdout);
return 0;
}