题解 P3750 【[六省联考2017]分手是祝愿】
我这个小菜鸡来水题解,完全是因为没有看到和我一样解dp的人QAQ
50\% 的数据
对于
注意到
调和级数
100\% 的数据
从上面的分析来看,最优操作方案是唯一的。所以,如果我们按了本就该按的开关,最优方案就可以少按一次;反之,最优方案就要多按一次开关。所以,转移只跟最优方案需要按开关的次数有关。
于是,我们可以设计出一个
这个式子只对
不妨设
根据
最后一路推到
如果
利用数学归纳法。
所以
话说为什么我想不到题解里面那些简单的做法啊 TAT
代码
这个模数
- 是一个小质数,逆元均存在。
- 略大于
n ,不会出现恰好除以该模数的情况。
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int zxy = 100003;
int inv[zxy], a[zxy], n, dp[zxy];
vector< int > son[zxy];
int main(){
inv[0] = inv[1] = 1; n = readint();
for(int i=2; i<zxy; ++i)
inv[i] = (0ll+zxy-zxy/i)
*inv[zxy%i]%zxy;
int k = readint();
// dp[i] = 1+i*dp[i-1]/n+(n-i)*dp[i+1]/n
// (n-i)*dp[i+1] = n*dp[i]-n-i*dp[i-1]
for(int i=0; i<=k; ++i) dp[i] = i;
int_ a0 = 0, b0 = k, a1 = 1, b1 = 0;
for(int i=k+1; i<=n; ++i){
// dp[i-1] = a0*dp[k+1]+b0
// dp[ i ] = a1*dp[k+1]+b1
int_ a2 = (n*a1-i*a0)%zxy;
int_ b2 = (n*b1-n-i*b0)%zxy;
a2 = (a2*inv[n-i]%zxy+zxy)%zxy;
b2 = (b2*inv[n-i]%zxy+zxy)%zxy;
a0 = a1, b0 = b1; // 向前移动
a1 = a2, b1 = b2; // 向前移动
}
dp[k+1] = (zxy-b1)*inv[a1]%zxy;
for(int i=k+1; i<n; ++i){
int &t = dp[i+1] = (1ll*n*dp[i]
-n-1ll*i*dp[i-1])%zxy;
t = (0ll+t+zxy)*inv[n-i]%zxy;
}
for(int i=1; i<=n; ++i)
a[i] = readint();
for(int i=1; i<=n; ++i)
for(int j=1; j<=n/i; ++j)
son[i*j].push_back(i);
int cnt = 0;
for(int i=n; i>=1; --i){
cnt += a[i];
for(auto j : son[i])
a[j] ^= a[i];
}
int ans = dp[cnt];
for(int i=1; i<=n; ++i)
ans = 1ll*ans*i%zxy;
printf("%d\n",ans);
return 0;
}