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

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

题目大意:

\(n(n\le10^5)\)盏灯,编号为\(1\sim n\),给定它们的初始状态。对编号为\(i\)的灯进行操作时,会同时改变编号为\(i\)的约数的灯的状态。每次等概率随机操作一个开关,当能在\(k\)步内将所有灯都关掉时,采取最优策略。问关掉所有灯所需操作次数的期望\(\times n!\)在模\(100\:003\)意义下的值。

思路:

\(f_i\)表示从最少需要\(i\)次的情况转移到最少需要\(i-1\)次的期望次数。则有\(\frac in\)的概率刚好挑到需要关的灯,有\(\frac{n-i}n\)选到了不该选的灯。因此我们可以得到:
\[f_i=\frac in+\frac{n-i}n(f_i+f_{i+1}+1)\]
变形,得转移方程:
\[f_i=\frac{n+(n-i)f_{i+1}}i\]
另对于\(i\le k\),显然有\(f_i=1\)

考虑计算初始状况下最少所需操作次数,显然我们有“每次关闭编号最大灯”的贪心。因此可以\(\mathcal O(n\log n)\)枚举\(i\)的倍数来计算每个灯在这个贪心下是否会被直接操作到。将最小操作次数记作\(m\),答案即为\(\sum_{i=1}^m f_i\)

时间复杂度\(\mathcal O(n\log n)\)

源代码:

#include<cstdio>
#include<cctype>
#include<algorithm>
typedef long long int64;
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int N=1e5+1,mod=1e5+3;
int f[N];
bool b[N];
void exgcd(const int &a,const int &b,int &x,int &y) {
    if(!b) {
        x=1,y=0;
        return;
    }
    exgcd(b,a%b,y,x);
    y-=a/b*x;
}
inline int inv(const int &x) {
    int ret,tmp;
    exgcd(x,mod,ret,tmp);
    return (ret%mod+mod)%mod;
}
int main() {
    const int n=getint(),k=getint();
    int fac=1,tot=0;
    for(register int i=1;i<=n;i++) {
        b[i]=getint();
        fac=(int64)fac*i%mod;
    }
    for(register int i=n;i;i--) {
        for(register int j=i*2;j<=n;j+=i) {
            b[i]^=b[j];
        }
        tot+=b[i];
    }
    std::fill(&f[1],&f[k]+1,1);
    for(register int i=n;i>k;i--) {
        f[i]=(int64)(n+(int64)(n-i)*f[i+1]%mod)*inv(i)%mod;
    }
    int ans=0;
    for(register int i=1;i<=tot;i++) {
        (ans+=f[i])%=mod;
    }
    ans=(int64)ans*fac%mod;
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/skylee03/p/9257205.html