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

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

感觉无从下手的概率题。

想清楚本质并不难。

 

瞎按不好考虑。反正有一般的是要直接做的。就考虑最优情况怎么按。

一个灯最多按一次。所以,最优策略次数一定小于等于n

从大到小依次按灭即可。把约数改变状态。

约数处理,枚举i从1~n,枚举倍数加入vector O(nlogn)

(听说可以拿到80pts了。。)

随机的部分怎么办?
发现,最优的策略一定要按特定的几次。并且这些次数按的先后顺序其实没有关系。

最优情况需要按的次数从多到少,所以,这就有了topo的性质!

而且,一旦最优策略的剩下的步数确定,答案的期望步数就确定了。与具体局面无关(并不关系到底最优策略怎么操作)!!

事情就比较好办了。

假如设f[i]表示,最优策略还要进行i次操作时,到终点的次数。

然而转移成环,而且没有办法破环(转化成kx+b的线性式可以,但是递推项在分母位置,没有办法取模)

所以,就一步一步来,设f[i]表示,最优策略还要进行i次操作时,变成操作i-1次期望次数。

f[i]=i/n*1+(n-i)/n*(f[i]+f[i+1]+1)

因为,没有先后顺序,所以i个中直接选择哪一个都可以。转移没有问题。

 

移项之后,即可倒序递推求解。

f[n]=1 假如剩下n步的话,那必然每个都要按一次就是最优解,所以,无论怎么按,都能到下一层。

最后,选取need~k这一段的f值。再加上k,再处理阶乘。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100000+5;
const int mod=100003;
int n,k;
vector<int>mem[N];
int inv[N];
ll f[N];
int a[N];
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j+=i)
            mem[j].push_back(i);
    inv[1]=1;
    for(int i=2;i<=n;i++) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
    int nd=0;
    for(int i=n;i>=1;i--){
        if(a[i]){
            for(int j=0;j<(int)mem[i].size();j++){
                
                a[mem[i][j]]^=1;
            }
            nd++;
        }
    }
    ll ans=0;
    //cout<<nd<<endl;
    if(nd<=k){
        ans=nd;
    }
    else{
        f[n]=1;
        for(int i=n-1;i>=1;i--) f[i]=((ll)n+(ll)(n-i)*f[i+1]%mod)%mod*inv[i]%mod;
        for(int i=nd;i>k;i--) ans=(ans+f[i])%mod;
        ans=(ans+k)%mod;
    }
    for(int i=1;i<=n;i++) ans=(ans*i)%mod;
    printf("%lld",ans);
    return 0;
}

总结:
1.发现最优策略,发现操作次数一定,答案就一定。所以可以就着最优操作次数下手。(亮灯的数量显然不行。因为变化太大。)这样设计也满足选中就进入下一层,也可能原地不动或者返回上一层。

2.状态设计不一定要直接指向终点,如果后继比较显然的话,可以尝试这样设计状态。

 

posted @ 2018-10-24 15:42  *Miracle*  阅读(339)  评论(0编辑  收藏  举报