2018.9.1zrnoip模拟T2

描述
给出一个长度为 N 的数列 a。

进行若干轮操作,每次操作在 [1,N] 中等概率选择一个使得 ai>0 的 i,并将 ai 减少 1。

问,期望多少次操作后 a1 被减成了零。

输入格式
第一行一个整数 N。

第二行 N 个整数,第 i 个为 ai。

输出格式
一行一个整数,表示答案。为避免精度误差,答案对 323232323 取模。

即设答案化为最简分式后的形式为 ab a b ,其中 a 和 b 互质。输出整数 xx 使得 bx≡a(mod323232323) 且 0≤x<323232323。可以证明这样的整数 xx 是唯一的。

样例一
input
3
2 3 3
output
202020207
限制与约定
每个测试点 10 分,共 10 个测试点:
这里写图片描述
对于所有的数据,有:1≤N,1≤ai≤5×10^5。
时间限制:1s
空间限制:256MB


我们先考虑一个30分的暴力
dp
这应该很好写….
我们考虑正解
假如我们能把每个 ai a i 被选的期望个数求出来那么答案就很好算了:

ans=i=2nfi+a1 a n s = ∑ i = 2 n f i + a 1

问题就在于 fi f i 怎么求
既然我们可以把问题独立出来,是否可以把求解 fi f i 给独立出来呢?
我们发现这个问题中唯一的规律就是 1 1 必定被拿a1
我们在结合 n<=2 n <= 2 的部分分
我们是不是可以对于每个 ai a i ,都拿他和 a1 a 1 算出 fi f i
我们思考一下,如果我们把操作序列中和 a1 a 1 ai a i 无关的拿掉
那么相当于我们选取 a1 a 1 ai a i 的概率都是 12 1 2
!!!
那我们就可以独立做了
但是这样分开dp只有60分
我们考虑怎么优化
我们把问题转化一下
题意相当于是我们从 (a1,ai) ( a 1 , a i ) 出发,每次向下向右随机游走
走到坐标轴结束
走到 (0,a) ( 0 , a ) 的贡献是 aia a i − a ,走到 (a,0) ( a , 0 ) 的贡献是 ai a i
于是我们可以列出 ai a i 的贡献式子:
i=0ai1iCia11+i2a1+i+ai(1i=0ai1Cia11+i2a1+i) ∑ i = 0 a i − 1 i ∗ C a 1 − 1 + i i 2 a 1 + i + a i ∗ ( 1 − ∑ i = 0 a i − 1 C a 1 − 1 + i i 2 a 1 + i )

我们发现 ai a i 增加1的时候式子的转移是 O(1) O ( 1 )
于是就可以线性求解啦~

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
const int p = 323232323;
int n;
int Max;
int a[501000] , bin[1001000] , fac[1001000] , Fac[1001000];
int f[501000] , suf[501000];
int ans[501000];
int read(){int sum = 0;char c = getchar();bool flag = true;while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();if(flag) return sum;else return -sum;}
int Pow(int a,int x){int sum = 1;while(x){if(x & 1) sum = 1ll * sum * a % p;a = 1ll * a * a % p;x >>= 1; }return sum;}
int C(int a,int b){if(b == 0) return 1;return 1ll*fac[a]%p*Fac[b]%p*Fac[a-b]%p;}
int main()
{
    n = read();rep(i,1,n) a[i] = read(),Max = max(Max,a[i]);
    bin[0] = 1;rep(i,1,2*Max) bin[i] = (bin[i-1]<<1)%p;
    rep(i,1,2*Max) bin[i] = Pow(bin[i],p-2);
    fac[0] = 1;rep(i,1,2*Max) fac[i] = 1ll * fac[i-1] * i % p;
    rep(i,1,2*Max) Fac[i] = Pow(fac[i],p-2);
    rep(i,0,Max) f[i] = 1ll * bin[a[1]+i] * C(a[1]+i-1,i)%p;
    rep(i,0,Max) suf[i] = (suf[i-1]+f[i])%p;
    ans[1] = (1-f[0]+p)%p;
    rep(i,2,Max)
    {
        ans[i] = ans[i-1];
        ans[i] = ans[i] + 1;
        ans[i] = ans[i] - suf[i-1];
        ans[i] = (ans[i]%p+p)%p;
    }
    int tot = a[1];
    rep(i,2,n) tot = (tot + ans[a[i]])%p;
    printf("%d\n",tot);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值