Div2 1000pts
题意:
要求1~N的排列p,对于一个位置i,都有条件p[i]
数据范围:
N will be between 1 and 200, inclusive.
pos will contain between 1 and N-1 elements, inclusive.
Elements of pos will be distinct.
Each element of pos will be between 1 and N-1, inclusive.
(pos用来记录p[i]>p[i+1]的位置i,其余i都是p[i]
样例解释:
5
{3}
Returns: 9
求1~5的排列使得p(1) > p(2), p(2) > p(3), p(3) < p(4), p(4) > p(5),于是可以得到如下9个排列:
{3,2,1,5,4}
{4,2,1,5,3}
{4,3,1,5,2}
{4,3,2,5,1}
{5,2,1,4,3}
{5,3,1,4,2}
{5,3,2,4,1}
{5,4,1,3,2}
{5,4,2,3,1}
思路:
我首先想到的是拓扑序计数,因为p[i]
#include <bits/stdc++.h>
using namespace std;
const int M = 210;
const int MOD = 1e9+7;
class PermutationCountsDiv2 {
public:
int countPermutations( int N, vector <int> pos ) ;
};
int PermutationCountsDiv2::countPermutations(int N, vector <int> pos) {
bool b[M];
memset(b, 0, sizeof(b));
for (int i = 0, sz = pos.size(); i < sz; i++)
b[pos[i]] = 1;
int f[M][M];
memset(f, 0, sizeof(f));
f[1][1] = 1;
for (int i = 2; i <= N; i++)
for (int j = 1; j <= i; j++){
if (b[i-1])
for (int k = 1; k < j; k++)
f[i][j] = (f[i][j]+f[i-1][k])%MOD;
else
for (int k = j; k < i; k++)
f[i][j] = (f[i][j]+f[i-1][k])%MOD;
}
int ans = 0;
for (int i = 1; i <= N; i++)
ans = (ans+f[N][i])%MOD;
return ans;
}
Div1 250pts
一道非常大可的概率DP。
题意:
一个数组,第一次random取一个数,之后每次在剩下的数里面random取,如果rand到的数数组下标大于上一次取的下标,就继续,如果取到的下标小于上一次或者没东西取了就退出rand过程,这次过程的分数是所取的所有元素的和。求一次过程选出的sum的期望。
数据范围:
The number of elements in d will be between 1 and 250, inclusive.
Each element of d will be between 1 and 1,000, inclusive.
样例解释:
{1,1,1}
Returns: 1.6666666666666667
他所有rand的可能:
1.有1/3可能性, Charlie chooses pancake 0 first. In this case he won’t be able to add any more pancakes and the total deliciousness of his serving of pancakes will be 1.
2.With probability 1/3, Charlie chooses pancake 1 first. What happens in the second round? With probability 1/2 he will choose pancake 0 and with probability 1/2 it will be pancake 2. In the first case the total deliciousness of Charlie’s pancakes will be 2, in the second case it will be 1.
3.With probability 1/3, Charlie chooses pancake 2 first. If he chooses pancake 0 next, the total deliciousness of his pancakes will be 2. If he happens to choose pancake 1 next (followed by pancake 0 in the third round), the total deliciousness will be 3.
Summing this up, we get the expected deliciousness to be
他所有rand的可能情况:
1.有1/3可能性,他先选了第0只煎饼。然后他无论选哪只煎饼都直接退出过程,所以sum为1。
2.有1/3可能性,他先选了第1只煎饼。然后他有1/2可能性选第0只,得到sum值2,或者1/2可能性选第2只,退出,得到sum为1。
3.有1/3可能性,他先选了第2只煎饼。同理,他可以选0,退出,或者选1再选0。
总期望:1/3 * (1) + 1/3 * (1/2 * 1 + 1/2 * 2) + 1/3 * (1/2 * 2 + 1/2 * 3) = 5/3 = 1.666…
思路:
经过一番挣扎才做对的题,虽然只是基础DP。
首先可以确定DP数组长这样:f[i][j]表示最后选的是i,已经选了j个饼并且还没有退出的价值。
但是如果单纯这样,状态转移会出现错误,于是在改成Dfs疯狂T飞之后,在f[][]数组基础上加上了rate[i][j],表示最后选的是i,已经选了j个饼并且还没有退出,达到这种状态的概率。于是DP就很好写啦:
初始化:
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 310;
int n, a[N];
double f[N][N], rate[N][N], ans;
class RandomPancakeStack {
public:
double expectedDeliciousness( vector <int> d ) ;
};
double RandomPancakeStack::expectedDeliciousness(vector <int> d) {
n = d.size();
for (int i = 1; i <= n; i++)
a[i] = d[n-i];
ans = 0;
memset(f, 0, sizeof(f));
memset(rate, 0, sizeof(rate));
rate[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++){
for (int k = j-1; k < i; k++){
rate[i][j] += rate[k][j-1]/(n-j+1);
f[i][j] += f[k][j-1]/(n-j+1)+rate[k][j-1]/(n-j+1)*a[i];
}
}
ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j < i; j++)
ans += f[i][j]*(i-j)/(n-j);
ans += f[n][n];
return ans;
}
Div1 500pts
终于要完结一篇了,鼓掌鼓掌
题意:
和Div2 1000pts的题一模一样,但是数据范围变大, N≤1000,000,pos.size()≤2500 N ≤ 1000 , 000 , p o s . s i z e ( ) ≤ 2500
思路:
N比较大,但是pos的大小比较少,也就是说可以把N长度的排列变成pos.size()+1段,可以像下面那张图这样理解。
分完组之后可以发现,每个组自己的排列是固定的,二前后两组唯一的联系就是后一组的开头要大于前一组的结尾。所以题解想到了容斥,我想不到。
对于前i组,先把所有元素放在一起排列,这时多考虑了第一组的开头大于第二组的情况,第二组的开头大于第三组的情况,等等等等。于是我们减去第一组排好了,在此基础上加上第二组到第i组排列的情况,于是现在第一组开头大于第二组的情况排除了,但是又多减去了后面几组全部排好和后面有一些组排好的情况,于是一直这么做就可以得到正确答案了。
然而正规的容斥证明并不会,只能用用小学生容斥。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2510;
const int M = 1e6+10;
const ll MOD = 1e9+7;
ll n, a[N], f[N], fac[M], invfac[M];
ll Pow(ll x, ll p)
{
ll ret = 1;
while (p > 0){
if (p&1) ret = ret*x%MOD;
x = x*x%MOD;
p >>= 1;
}
return ret;
}
ll C(ll x, ll y)
{
return fac[x]*invfac[x-y]%MOD*invfac[y]%MOD;
}
class PermutationCounts {
public:
int countPermutations( int maxp, vector <int> pos ) ;
};
int PermutationCounts::countPermutations(int maxp, vector <int> pos) {
n = pos.size();
for (int i = 1; i <= n; i++)
a[i] = pos[i-1];
a[0] = 0; a[++n] = maxp;
sort(a, a+n+1);
fac[1] = 1;
for (int i = 2; i <= maxp; i++)
fac[i] = fac[i-1]*i%MOD;
invfac[0] = 1;
for (int i = 1; i <= maxp; i++)
invfac[i] = Pow(fac[i], MOD-2);
memset(f, 0, sizeof(f));
f[0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j < i; j++){
if ((i-j)%2)
f[i] = (f[i]+f[j]*C(maxp-a[j], a[i]-a[j]))%MOD;
else
f[i] = (f[i]+MOD-f[j]*C(maxp-a[j], a[i]-a[j])%MOD)%MOD;
}
return f[n];
}