BZOJ 4872 六省联考2017 分手是祝愿

Problem

BZOJ

Solution

感觉dp状态的设置好巧妙啊
首先要明确的是怎么计算最小步数。就是直接从n到1扫,如果有亮着的,就按这个开关,模拟一下是 O ( n ln n )
设f[i]表示在局面的最小步数为i时,转移到最少步数为i-1时的期望花费。只要是相应的灯,按的次序是不会影响答案的,那么i次就说明还需要按i个灯,则有i/n的概率到下一步,而有(n-i)/n的概率回到f[i+1]状态,则有
f [ i ] = i n + n i n ( f [ i + 1 ] + f [ i ] + 1 )
再移项即得 f [ i ] = ( n i ) f [ i + 1 ] + n i
然后这是一个差分序列,求和即可得到ans

Code

#include <cstdio>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=100010,mod=100003;
struct data{int v,nxt;}edge[maxn*20];
int n,k,p,cnt,ans,fac=1,head[maxn],a[maxn],f[maxn],inv[maxn];
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline void insert(int u,int v){edge[++p]=(data){v,head[u]};head[u]=p;}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    read(n);read(k);
    inv[0]=inv[1]=1;f[n]=1;
    for(rg int i=1;i<=n;i++)
    {
        read(a[i]);fac=(ll)fac*i%mod;
        for(rg int j=i;j<=n;j+=i) insert(j,i);
        if(i^1) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
    }
    for(rg int i=n;i;i--)
      if(a[i])
      {
        cnt++;
        for(int j=head[i];j;j=edge[j].nxt) a[edge[j].v]^=1;
      }
    if(cnt<=k){printf("%lld\n",(ll)cnt*fac%mod);return 0;}
    for(rg int i=n-1;i>k;i--) f[i]=((ll)(n-i)*f[i+1]+n)%mod*inv[i]%mod;
    for(rg int i=cnt;i>k;i--) ans=pls(ans,f[i]);
    printf("%lld\n",(ll)(ans+k)*fac%mod);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/as_a_kid/article/details/80919140