• [六省联考2017]分手是祝愿


    题目

    发现无论是什么时候,毕姥爷的概率题我就是不会做

    首先先看到一个显然的性质,就是一个开关最多被操作一次,于是整个序列最多也就被操作(n)

    看到有(50)(k=n),于是只需要求一下最少几步关掉全部的灯即可

    这里需要一个贪心,显然我们需要先去关编号大的灯,编号大的灯只会影响它的约数不会影响编号更大的灯,这样贪心可以保证每个开关只会被操作一次

    调和级数存一下约数,暴力一下就做完了,这样就有(80)分了

    我们考虑我们随机操作有什么用处

    假设我们求出来原来最优需要(x)步就能全部关掉,那么我们随机操作到的可能恰好是我们需要关掉的,操作之后就只需要(x-1)步了,如果操作到的是一个原来不需要关掉的,我们就必须额外操作一次,就需要(x+1)步了

    所以实际上这个问题的具体的开关状态并没有什么关系,只和当前最有策略需要多少步有关系

    (dp_i)表示到(i)步这个状态期望经过多少次

    显然有

    [dp_i=frac{(i+1)dp_{i+1}}{n}+frac{(n-i+1)dp_{i-1}}{n} ]

    初值(dp_x=1)

    很好理解,(dp_{i+1})这个状态有(frac{i+1}{n})的概率选择到一个需要操作的灯转移到(dp_i)(dp_{i-1})同理

    可以直接高斯消元,但是复杂度(O(n^3))显然不科学

    于是我就不会了,本以为这个柿子有奇妙的性质,但是无论怎么化都发现这是一个需要高斯消元的垃圾柿子

    于是抄题解

    换个角度考虑,设(f_i)表示从(i)这个状态转移到(i-1)的期望次数

    (frac{i}{n})的概率直接转移过去,这里的消耗是(frac{i}{n})

    (frac{n-i}{n})的概率转移到(i+1),这里的消耗是(frac{n-i}{n}),之后得从(i+1)转移到(i-1),需要自然是(f_{i+1}+f_{i})

    于是

    [f_i=1+frac{(n-i)(f_i+f_{i+1})}{n} ]

    随便化一化,发现

    [f_i=frac{f_{i+1} imes n}{i}-f_{i+1} ]

    可以直接递推了

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #define maxn 100005
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    const LL mod=100003;
    inline int read() {
    	int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    void exgcd(LL a,LL b,LL &x,LL &y) {if(!b) {x=1,y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
    inline LL inv(LL a) {LL x,y;exgcd(a,mod,x,y);return (x%mod+mod)%mod;}
    LL fac;
    int a[maxn],n,m;
    int now;
    std::vector<int> v[maxn];
    LL dp[maxn];
    int main() {
    	n=read(),m=read();
    	fac=1;for(re int i=1;i<=n;i++) fac=(fac*i)%mod;
    	for(re int i=1;i<=n;i++)
    		for(re int j=i;j<=n;j+=i) v[j].push_back(i);
    	for(re int i=1;i<=n;i++) a[i]=read();
    	for(re int i=n;i;--i) {
    		if(!a[i]) continue;
    		now++;
    		for(re int j=0;j<v[i].size();j++) a[v[i][j]]^=1;
    	}
    	if(now<=m) {printf("%lld
    ",(LL)now*fac%mod);return 0;}
    	dp[n]=1;
    	for(re int i=n-1;i>=1;--i) dp[i]=1ll+(dp[i+1]+1ll)*n%mod*inv(i)%mod-(dp[i+1]+1ll),dp[i]=(dp[i]+mod)%mod;
    	LL ans=m;
    	for(re int i=now;i>m;--i) ans=(ans+dp[i])%mod;
    	printf("%lld
    ",ans*fac%mod);
    	return 0;
    }
    
  • 相关阅读:
    python之各种包
    正则表达式
    import/模块的导入
    迭代器/可迭代对象/生成器
    Day2 列表list
    Day1 字符串格式化
    Day1 字符编码及编码函数
    Python 学习笔记 之 随着学习不断更新的Python特性搜集
    Day1 input&print
    Newtonsoft.Json日期转换
  • 原文地址:https://www.cnblogs.com/asuldb/p/10409063.html
Copyright © 2020-2023  润新知