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

题目:
https://www.luogu.org/problemnew/show/P3750

分析:
显然按一个开关不能使得比他大的数熄灭。所以最优方案一定是每次选出最大的数按掉。
可以用枚举倍数,然后用vector存某个数的约数,求出需要按的开关数 c n t cnt cnt
f [ i ] f[i] f[i]表示还有 i i i个开关需要按到还有 i − 1 i-1 i1个开关需要按的情况期望需要多少步,
显然
f [ i ] = i n + n − i n ∗ ( f [ i + 1 ] + f [ i ] + 1 ) f[i]=\frac{i}{n}+\frac{n-i}{n}*(f[i+1]+f[i]+1) f[i]=ni+nni(f[i+1]+f[i]+1)
前面的表示直接按到了需要按的开关,后面表示没有按到,那么就需要按 i + 1 i+1 i+1个开关到 i i i,再从 i i i i − 1 i-1 i1
其中, f [ n ] = 1 f[n]=1 f[n]=1,若 i < = k i<=k i<=k f [ i ] = 1 f[i]=1 f[i]=1
答案就是 ∑ i = 1 c n t f [ i ] \sum_{i=1}^{cnt}f[i] i=1cntf[i]

代码:

// luogu-judger-enable-o2
#include 
#include 
#include 
#include 
#define LL long long

const int maxn=1e5+7;
const LL mod=100003;

using namespace std;

int n,k,cnt;
int a[maxn];
LL f[maxn],ans;

vector  p[maxn];

LL ksm(LL x,LL y)
{
    if (y==1) return x;
    LL c=ksm(x,y/2);
    c=c*c%mod;
    if (y&1) c=c*x%mod;
    return c;
}

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)
        {
            p[j].push_back(i);
        }
    }
    for (int i=n;i>0;i--)
    {
        if (a[i])
        {
            cnt++;
            for (int j=0;j0;i--)
    {
        f[i]=(f[i+1]*(LL)(n-i)+(LL)n)%mod*ksm(i,mod-2)%mod;
    }
    if (cnt<=k) ans=cnt;
    else
    {
        for (int i=k+1;i<=cnt;i++) ans=(ans+f[i])%mod;
        ans=(ans+k)%mod;
    }
    for (int i=1;i<=n;i++) ans=ans*(LL)i%mod;
    printf("%lld\n",ans);
}

你可能感兴趣的:(DP)