如果你点进这一题
解释:
50分做法:dfs暴力枚举每个数选和不选即可。
100分做法:用二项式定理求解
小G在这里稍微证明一下,我们设函数
f
(
x
)
=
(
1
+
a
1
x
)
(
1
+
a
2
x
)
⋯
(
1
+
a
n
x
)
f(x) = (1+a_1x) (1+a_2x)\cdots(1+a_nx)
f(x)=(1+a1x)(1+a2x)⋯(1+anx)
对它展开,在没有合并同类项的之前,每个项的系数其实都是一个子序列的乘积。
例如
n
=
2
n=2
n=2时有:
f
(
x
)
=
(
1
+
a
1
x
)
(
1
+
a
2
x
)
=
1
+
a
1
x
+
a
2
x
+
a
1
a
2
x
2
f(x) = (1+a_1x) (1+a_2x) = 1+a_1x+a_2x+a_1a_2x^2
f(x)=(1+a1x)(1+a2x)=1+a1x+a2x+a1a2x2
所以,求所有子序列的和只需要求 f ( 1 ) f(1) f(1)即可。但是注意这上面其实多了一个 1 1 1,这是空的序列的贡献,只需要减去这个1即可。
最后我们只需要求
f
(
1
)
−
f
(
0
)
=
(
1
+
a
1
x
)
(
1
+
a
2
x
)
⋯
(
1
+
a
n
x
)
−
1
f(1) - f(0) = (1+a_1x) (1+a_2x)\cdots(1+a_nx) - 1
f(1)−f(0)=(1+a1x)(1+a2x)⋯(1+anx)−1
最后注意每一步都要进行取模操作。
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL MOD = 1e9+7;
const LL N = 1010000;
LL n,a[N],ans = 1;
void solve()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
ans = ans*( (a[i] + 1)%MOD );
ans %= MOD;
}
cout<<( (ans - 1)%MOD + MOD ) % MOD;
}
signed main()
{
solve();
return 0;
}
氪金
40分做法:判断输入的数据是否全满足 s i = a i s_i=a_i si=ai或有卡池满足 a i = 0 a_i=0 ai=0分别输出 1 1 1和 0 0 0即可。
100分做法:这里能求抽到的概率很麻烦。那么我们求反就好了,也就是求失败的概率。失败就是每次都抽不到,第
i
i
i次抽不到的概率是
s
i
−
a
i
s
i
\frac{s_i-a_i}{s_i}
sisi−ai。然后进行累乘就好了。因为模数是质数,分母的逆元可以用费马小定理得出。
最后成功的概率就是
a
n
s
=
1
−
∏
i
=
1
n
s
i
−
a
i
a
i
ans =1 - \prod_{i=1}^{n} \frac{s_i-a_i}{a_i}
ans=1−i=1∏naisi−ai
注意每一步都要进行取模操作。
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e6+10000;
const int MOD=1e9+7;
LL s[N],a[N],n;
LL quick(LL a,LL b)
{
LL res=1;
while(b)
{
if(b&1) res = res*a % MOD;
a = a*a % MOD;
b >>= 1;
}
return res;
}
void solve()
{
scanf("%d",&n);
LL ans=1;
for(int i=1;i<=n;i++) scanf("%lld",&s[i]);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
{
ans = ans * (s[i]-a[i]) % MOD * quick(s[i],MOD-2) % MOD;
}
cout<<( (1-ans) + MOD ) % MOD<<endl;
}
signed main()
{
solve();
return 0;
}
风吹麦浪
30分做法:应用的是哈夫曼树的原理,要求最小值就是每次选取序列中两个最小的值进行相加,最后的到的结果就是最小值,为了方便计算,我们可以使用小根堆或优先队列来计算,每次选取堆顶的值进行相加即可。时间复杂度 O ( n ∗ l o g n ) O(n*logn) O(n∗logn)
100分做法:显然两个点合并的结果对答案的贡献肯定会越来越大,我们可以维护两个队列,第一个队列 q 1 q_1 q1 初始是每堆果子的数(从小到大排序),第二个队列 q 2 q_2 q2 初始为空,存放每次合并后的数。虽然 1 < = n < = 1 0 7 1<=n<=10^7 1<=n<=107,但由于 1 < = a i < = 1 0 5 1<=a_i<=10^5 1<=ai<=105,排序我们可以使用桶排序。这里由于排了序, q 1 q_1 q1 队列始终是单调递增的,并且 q 2 q_2 q2队列因为上面一条性质也必然是单调递增的。现在我们知道了它们都有单调性,那么合并时选择的数就很显然了。看 q 1 , q 2 q_1,q_2 q1,q2 队头元素大小关系选择较小的两个数合并,将合并得到的数插入到 q 2 q_2 q2 的队尾。如此循环,直到 q 1 q_1 q1 为空, q 2 q_2 q2 只剩一个元素为止。时间复杂度为 O ( n ) O(n) O(n)
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N = 105000;
const LL MOD = 1e6+7;
LL ans = 0;
LL n,a[N];
queue<int>q1,q2;
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
LL tag;
scanf("%lld",&tag);
a[tag]++;
}
for(int i=1;i<=100000;i++)
{
while( a[i] )
{
q1.push(i);
a[i]--;
}
}
ans = 0;
for(int k=1;k<n;k++)
{
int x,y;
if((q1.front() < q2.front() && !q1.empty()) || q2.empty())
{
x = q1.front();
q1.pop();
}
else
{
x = q2.front();
q2.pop();
}
if((q1.front() < q2.front() && !q1.empty()) || q2.empty())
{
y = q1.front();
q1.pop();
}
else
{
y = q2.front();
q2.pop();
}
ans += (x+y);
q2.push(x+y);
}
printf("%lld",ans);
}
signed main()
{
solve();
return 0;
}