bzoj4872 [Shoi2017]分手是祝愿(贪心+期望dp)

首先考虑k=n的话最优解是怎样的。
因为拉一盏灯只会影响到比它标号小的灯和自己。
所以我们按标号从大到小考虑。如果一盏灯还亮着就必须拉,并且把影响到的灯改变。这样就可以得到目前状态的最小操作次数了。
设f[i]表示剩余最小操作次数为i时还期望操作多少步,可以列出方程:
f[i]=inf[i1]+ninf[i+1]+1 f [ i ] = i n ∗ f [ i − 1 ] + n − i n ∗ f [ i + 1 ] + 1
特别的对于i<=k,f[i]=i,f[n]=f[n-1]+1.
可以高斯消元 O(n3) O ( n 3 )
考虑进一步优化,原方程变一变可以得到
f[i]f[i1]=nin(f[i+1]f[i1])+1 f [ i ] − f [ i − 1 ] = n − i n ( f [ i + 1 ] − f [ i − 1 ] ) + 1
我们对f数组差分一下,令g[i]=f[i]-f[i-1]。
那么有 g[i]=nin(g[i+1]+g[i])+1 g [ i ] = n − i n ( g [ i + 1 ] + g [ i ] ) + 1
g[i]=((ni)g[i+1]+n)/i g [ i ] = ( ( n − i ) g [ i + 1 ] + n ) / i
可以递推了,特别的 g[n]=1,g[i]=1,i<=k g [ n ] = 1 , g [ i ] = 1 , i <= k
复杂度 O(nn) O ( n n )

#include 
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
#define mod 100003
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(T==S){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,k,g[N],inv[N],ans=1,a[N];
inline void inc(int &x,int y){x+=y;x%=mod;}
int main(){
//  freopen("a.in","r",stdin);
    n=read();k=read();inv[1]=1;
    for(int i=2;i<=n;++i) inv[i]=(ll)inv[mod%i]*(mod-mod/i)%mod;
    int m=0;for(int i=1;i<=n;++i) a[i]=read(),ans=(ll)ans*i%mod;
    for(int i=n;i>=1;--i){
        if(!a[i]) continue;++m;
        for(int x=1;x*x<=i;++x){
            if(i%x) continue;a[x]^=1;
            if(x*x!=i) a[i/x]^=1;
        }
    }if(m<=k){ans=(ll)ans*m%mod;printf("%d\n",ans);return 0;}
    g[n]=1;
    for(int i=n-1;i>k;--i)
        g[i]=((ll)(n-i)*g[i+1]+n)%mod*inv[i]%mod;
    for(int i=1;i<=k;++i) g[i]=1;
    for(int i=1;i<=m;++i) inc(g[i],g[i-1]);
    ans=(ll)g[m]*ans%mod;
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(bzoj,贪心,概率与期望)