1005 Everything Is Generated In Equal Probability
题意
太长啦,就是模拟运行一个程序输出结果的期望值。
思路
绝大多数人都是打表找规律吧,反正最终结果就是
n
2
−
1
9
\frac {n^2-1} 9
9n2−1
然而我在现场的时候头铁自己推公式,就差一点就推出来了。。。。。赛后特别不甘心,就用自己推出来的公式重做了一次。
推导过程
①首先可以求出长度为n的序列,它的逆序数期望为
n
(
n
−
1
)
4
\frac {n(n-1)} 4
4n(n−1)
这是怎么来的呢,首先长度为1的时候贡献肯定为0,也就是
E
[
1
]
=
0
E[1]=0
E[1]=0。而长度为2时,看作在长度为1的基础上插入一个数,那么共有两个位置可以插入,那么这个序列为{1,2}和{2,1},其中{2,1}的贡献为1,那么
E
[
2
]
=
E
[
1
]
+
1
2
E[2]=E[1]+\frac 1 2
E[2]=E[1]+21。那么多推几个就可以得到通项
E
[
n
]
=
E
[
n
−
1
]
+
n
−
1
2
E[n]=E[n-1]+\frac {n-1} 2
E[n]=E[n−1]+2n−1,那么再等差数列求和一下,就可以知道
E
[
n
]
=
n
(
n
−
1
)
4
E[n]=\frac {n(n-1)} 4
E[n]=4n(n−1)
于是乎完成了万里长征的第一步
②我们设
f
[
i
]
f[i]
f[i]为长度为
i
i
i的序列运行程序的结果的期望
怎么求
f
[
n
]
f[n]
f[n]的通项呢?首先
f
[
0
]
=
f
[
1
]
=
0
f[0]=f[1]=0
f[0]=f[1]=0。
设当前数组长度为n(n和N代表的意义不同),我们想要求
f
[
n
]
f[n]
f[n],按照代码的意思,我们需要取这个数组的子序列递归运行求逆序数。若你从这n个数中取出r个数进行递归求解,本质上与一开始直接放入r个数求解是一样的,也就意味着得到的值就是
f
[
r
]
f[r]
f[r]。
那么从n个数里取长度为r的序列共有
C
n
r
C_n^r
Cnr种,题目还说了自己本身和空串都算作是子序列,n个数的子序列个数为
2
n
2^n
2n。嚯嚯嚯,这样
f
[
n
]
f[n]
f[n]的通项也就可以得到了
f
[
n
]
=
n
(
n
−
1
)
4
+
∑
i
=
1
n
C
n
i
f
[
i
]
2
n
f[n]=\frac {n(n-1)} 4+\frac {\sum_{i=1}^n{C_n^if[i]}}{2^n}
f[n]=4n(n−1)+2n∑i=1nCnif[i]
再将右侧的一个
f
[
n
]
f[n]
f[n]移项化简得到
f
[
n
]
=
n
(
n
−
1
)
4
2
n
2
n
−
1
+
∑
i
=
1
n
−
1
C
n
i
f
[
i
]
2
n
−
1
f[n]=\frac {n(n-1)} {4} \frac{2^n}{2^n-1}+\frac {\sum_{i=1}^{n-1}{C_n^if[i]}}{2^n-1}
f[n]=4n(n−1)2n−12n+2n−1∑i=1n−1Cnif[i]
③那么我们所求的最终答案就是
a
n
s
=
1
N
∑
i
=
1
N
f
[
i
]
ans=\frac 1 N \sum_{i=1}^{N} f[i]
ans=N1i=1∑Nf[i]
推导完毕,接下来只要用
O
(
n
2
)
O(n^2)
O(n2)预处理出
f
[
i
]
f[i]
f[i],再求一次前缀和就好了,理论上这个式子还可以化简,但是实在化不动了,反正这个复杂度也能轻松过
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int maxn = 3e3+20;
ll fa[maxn];
ll ifa[maxn];
ll f[maxn];
ll qmod(ll a,ll b)
{
ll c=1;
while(b)
{
if(b&1)
{
c=c*a%mod;
}
a=a*a%mod;
b>>=1;
}
return c;
}
inline add(ll &a,ll b)
{
a+=b;
if(a>mod)
a-=mod;
}
void init()
{
f[1]=0;
f[0]=0;
fa[0]=ifa[0]=1;
for(int i=1;i<=3000;i++)
{
fa[i]=fa[i-1]*i%mod;
}
ifa[3000]=qmod(fa[3000],mod-2);
for(int i=3000-1;i>0;i--)
{
ifa[i]=ifa[i+1]*(i+1)%mod;
}
for(int i=2;i<=3000;i++)
{
f[i]=i*(i-1)*qmod(4,mod-2)%mod*qmod(2,i)%mod;
for(int j=1;j<i;j++)
{
add(f[i],fa[i]*ifa[i-j]%mod*ifa[j]%mod*f[j]%mod);
}
f[i]=f[i]*qmod((qmod(2,i)-1),mod-2)%mod;
}
for(int i=2;i<=3000;i++)
{
add(f[i],f[i-1]);
}
}
int main() {
ll N;
init();
while(cin>>N)
{
cout<<f[N]*qmod(N,mod-2)%mod<<endl;
}
return 0;
}
1011 Keen On Everything But Triangle
题意
求区间内能构成的最大三角形的周长
题解
因为若无法构成三角形,当区间内的数满足斐波那契数列时,区间内的数才最多,然而即使这样,也不超过50个数,也就意味着区间内的数只要超过50个,一定能构成三角形。那么剩下的事情就是用主席树求区间第一大,第二大,一直往下跑就完事了。这里算三角形时范围是3e9,爆int了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 20;
int lson[maxn * 30], rson[maxn * 30], tree[maxn * 30];
ll a[maxn], ha[maxn], T[maxn];
int n, q, len, tot;
void init_hash()
{
sort(ha + 1, ha + 1 + n);
len = unique(ha + 1, ha + 1 + n) - ha - 1;
for (int i = 1; i <= n; i++)
{
a[i] = lower_bound(ha + 1, ha + 1 + len, a[i]) - ha;
}
}
void init()
{
tot = 0;
init_hash();
}
int build(int l, int r)
{
int rt = tot++;
tree[rt] = 0;
if (l != r)
{
int mid = (l + r) >> 1;
lson[rt] = build(l, mid);
rson[rt] = build(mid + 1, r);
}
return rt;
}
int update(int rt, int pos, int val)
{
int newroot = tot++;
int temp = newroot;
tree[newroot] = tree[newroot] + val; //更新区间和
int l = 1, r = len;
while (l < r) //这里迭代更新比递归快一点
{
int mid = (l + r) >> 1;
if (pos <= mid)
{
lson[newroot] = tot++;
rson[newroot] = rson[rt];
newroot = lson[newroot];
rt = lson[rt];
r = mid;
}
else
{
rson[newroot] = tot++;
lson[newroot] = lson[rt];
newroot = rson[newroot];
rt = rson[rt];
l = mid + 1;
}
tree[newroot] = tree[rt] + val;
}
return temp;
}
int query(int st, int end, int k)
{
int l = 1, r = len;
while (l < r)
{
int mid = (l + r) >> 1;
if (tree[lson[end]] - tree[lson[st]] >= k)
{
r = mid;
st = lson[st];
end = lson[end];
}
else
{
l = mid + 1;
k -= tree[lson[end]] - tree[lson[st]];
st = rson[st];
end = rson[end];
}
}
return l;
}
int main()
{
while(scanf("%d%d", &n, &q)!=EOF){
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
ha[i] = a[i];
}
init();
T[0] = build(1, len);
for (int i = 1; i <= n; i++)
{
T[i] = update(T[i - 1], a[i], 1);
}
for (int i = 1; i <= q; i++)
{
int l, r;
scanf("%d%d", &l, &r);
if (r - l + 1 < 3)
{
printf("-1\n");
continue;
}
else
{
int x, y, z;
ll ans = -1;
x = query(T[l - 1], T[r], r-l+1);
y =query(T[l - 1], T[r], r-l);
for (int i = r - l -1; i > 0;i--)
{
z = query(T[l - 1], T[r], i);
if(ha[x]<ha[y]+ha[z])
{
ans = ha[z] + ha[x] + ha[y];
break;
}
x = y, y = z;
}
printf("%lld\n", ans);
}
}
}
}