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

luogu

loj

可以发现在最优策略中,每种操作最多只会做一次,并且操作的先后顺序并不会影响答案,所以考虑从后往前扫,碰到一个\(1\)就对这个位置\(i\)进行操作,这样的操作一定是最优策略.记最优策略步数为\(m\),如果\(m\le k\),那么答案就是\(n!*m\) 这里有80'

然后考虑每次操作对其他位置是否操作的影响,打表可以发现在每个位置操作不会影响其他位置上是否操作,大概可以这样想,如果操作的位置\(x\)不是当前位置\(i\)的倍数那根本不可能有影响,如果是当前位置的倍数,那么可以递归考虑其他的是\(i\)倍数\(x\)因数的位置是否改变,发现如果不能再递归了,自己本身状态本来要反转的,因为\(x\)状态反转了,那么就不会改变当前位置状态,回溯的过程中,对于每个位置也只有自己和\(x\)的状态发生要反转,那么也不会影响

所以问题变成有\(a\)个位置要操作,每次等概率改变一个位置是否要操作,如果\(a\le k\)直接操作\(k\)步结束,问期望步数.这等价于每种状态期望出现次数\(+k\),设\(f_i\)\(i\)个位置要操作的状态期望出现次数,转移大概为\(f_i=[i-1>k]*\frac{n-(i-1)}{n}f_{i-1}+[i+1\le n]*\frac{i+1}{n}f_{i+1}+[i==m]\).然后列出一堆方程后,全部加在一起,消元后可得\(\frac{k+1}{n}f_{k+1}=1\),然后利用刚才的方程推出其他\(f_i\)即可.最终答案为\(n!(k+\sum_{i=k+1}^{n}f_i)\)

//i'm low low
#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=1e5+10,mod=100003;
LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
void ad(int &x,int y){x+=y,x-=x>=mod?mod:0;}
int fpow(int a,int b){int an=1;while(b){if(b&1) an=1ll*an*a%mod;a=1ll*a*a%mod,b>>=1;} return an;}
int ginv(int a){return fpow(a,mod-2);}
int n,kk,a[N],b[N],f[N],m;

int main()
{
	n=rd(),kk=rd();
	for(int i=1;i<=n;++i) a[i]=rd();
	for(int i=n;i;--i)
	{
		for(int j=i+i;j<=n;j+=i) a[i]^=b[j];
		if(a[i]) b[i]=1,++m;
	}
	if(m<=kk)
	{
		for(int i=1;i<=n;++i) m=1ll*m*i%mod;
		printf("%d\n",m);
	}
	else
	{
		ad(f[kk+1],1ll*n*ginv(kk+1)%mod);
		ad(f[kk+2],1ll*f[kk+1]*n%mod*ginv(kk+2)%mod);
		for(int i=kk+3;i<=n;++i)
			ad(f[i],1ll*(f[i-1]-1ll*(n-(i-2))*ginv(n)%mod*f[i-2]%mod-(i-1==m)+mod)%mod*n%mod*ginv(i)%mod);
		int ans=kk;
		for(int i=kk+1;i<=n;++i) ad(ans,f[i]);
		for(int i=1;i<=n;++i) ans=1ll*ans*i%mod;
		printf("%d\n",ans);
	}
    return 0;
}
posted @ 2019-11-11 22:13  ✡smy✡  阅读(105)  评论(0编辑  收藏  举报