luogu P3750 [六省联考 2017] 分手是祝愿
题面传送门
因为有50pts都是\(k=n\)所以先考虑这个。
考虑从后往前扫,如果扫到当前点发现当前点是亮的,那么当前点一定会操作一次,因为前面的点操作不到当前这个点。这样可以做出最小的步数。
然后交上去就有80pts了(
观察这个东西不难发现一个性质:如果\(i\)在最优方案中没有被按到但是我按了,那么最后的最优步数一定会+1
所以只要求出\(f_i\)表示现在有\(i\)个点的期望步数,设当前状态的最优步数为\(k\),则初始\(f_{k}=0\),有转移方程\(f_{i}=1+\frac{i}{n}f_{i-1}+\frac{n-i}{n}f_{i+1}\)
发现转移成环好像不太妙,直接暴力高斯消元好像能多5到10分,不过因为是一条链所以直接指定一个端点为主元然后在另一个端点解出来即可。
时间复杂度看你怎么枚举约数,蒟蒻写了个\(O(n\sqrt n)\)的。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (100000+5)
#define M (1<<20)
#define mod 100003
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,k,cnt,A[N+5],Id;ll Inv[N+5],F1[N+5],F2[N+5],Ans;
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
int main(){
freopen("1.in","r",stdin);
RI i,j;scanf("%d%d",&n,&k);for(i=1;i<=n;i++) scanf("%d",&A[i]);
for(i=n;i;i--){if(!A[i])continue;for(j=1;j*j<=i;j++) i%j==0&&(A[j]^=1,j*j!=i&&(A[i/j]^=1));cnt++;}
if(cnt<=k){for(i=1;i<=n;i++) cnt=1ll*cnt*i%mod;printf("%d\n",cnt);return 0;}for(i=1;i<=n;i++)Inv[i]=mpow(i);F2[k]=k;F1[k+1]=1;
for(i=k+1;i<n;i++) F1[i+1]=n*Inv[n-i]%mod*(F1[i]-F1[i-1]*i%mod*Inv[n]%mod)%mod,F2[i+1]=n*Inv[n-i]%mod*(F2[i]-1-F2[i-1]*i%mod*Inv[n]%mod)%mod;
Ans=(F2[n-1]-F2[n]+1)*mpow(F1[n]-F1[n-1])%mod;Ans=((Ans*F1[cnt]+F2[cnt])%mod+mod)%mod;for(i=1;i<=n;i++) Ans=Ans*i%mod;printf("%lld\n",Ans);
}