经分析可知:I.操作每个灯可看做一种异或状态 II.每个状态可看做是一些异或状态的异或和,而且每个异或状态只能由它本身释放或放入 III.每一种异或状态只有存在不存在两中可行状态,因此这些灯只有同时处于不存在才可以,而两种异或状态之间没有关系因此可以把这些状态看做一样的,因此counts的是异或状态数。
到这里为止我们可以得到一个简单的转移方程 f[i]=i/n*f[i-1]+(n-i)/i*f[i+1]+1 于是看起来似乎已经到了解决问题的时候,所以我就开始推.......然后就没有然后了,由这个式子出发的扔锅,永远没头.....
.最后知道正解是差分的我大概......我们可以这样想,从每个f[i]出发到达最后他一定是先从自己出发再到每个可能第一次到达i-1,在每个可能第一次到达i-2....而我们发现对于一个i到达i-1的期望次数是一定的因此我们可以从此入手 得到 g[i]=i/n+(n-i)/n(g[i+1]+g[i]+1) 这样我们就能用一个二阶递推来AC了
(*@ο@*) 哇~ 神?差分,让我推一年我也推不出来.......
#include<cstdio> #include<iostream> #define MAXN 100100 using namespace std; typedef long long LL; const LL P=100003; LL jie[MAXN],g[MAXN],f[MAXN],n,k; int now[MAXN]; inline LL ni(LL x) { LL y=P-2,ans=1;; while(y) { if(y&1)ans=ans*x%P; y>>=1; x=x*x%P; } return ans; } int main() { scanf("%lld%lld",&n,&k); jie[1]=1; for(LL i=2;i<=n;i++) jie[i]=jie[i-1]*i%P; for(LL i=1;i<=k;i++) g[i]=jie[n]; g[0]=0; g[n]=jie[n]; for(LL i=n-1;i>k;i--) g[i]=((n-i)*g[i+1]%P+n*jie[n]%P)%P*ni(i)%P; for(int i=1;i<=n;i++) scanf("%d",&now[i]); LL aim=0; for(int i=n;i>0;i--) if(now[i]) { aim++; int j=1; for(;j*j<i;j++) if(i%j==0) now[j]^=1,now[i/j]^=1; if(j*j==i) now[j]^=1; } LL ans=0; for(int i=0;i<=aim;i++) ans+=g[i]; ans%=P; printf("%lld",ans); return 0; }
时间: 2024-10-11 06:18:56