@computerkiller
2021-02-18T12:45:53.000000Z
字数 27170
阅读 577
数学 题解
改了标题之后里面的题目就杂起来了
那么问题来了 计算几何算不算数学题
套路题
考虑某个不为的物品的生成函数:
signed main(){read(n,m);for (int i = 1; i <= m; i++){int a,b;read(a,b);if (a <= n) t[a] = (t[a] + 1) % mod;if (b && a * (b + 1) <= n) t[a * (b + 1)] = (t[a * (b + 1)] - 1 + mod) % mod;}for (int i = 1; i <= n; i++)for (int j = i; j <= n; j += i)f[j] = (f[j] + t[i] * ksm(j / i,mod - 2) % mod) % mod;getexp(f,g,n + 1);for (int i = 1; i <= n; i++)writeln(g[i]);return 0;}
神仙题 膜拜\color{black}\text{f}\color{red}\text{ffxk} 众所周知 qy稳了
草这题的k怎么是2000啊 还要爆踩标算才行
给那题加强一下好了 我们要求的是:
那么 即 也就是 的 阶差分是
所以 的通项是一个 次的多项式 我们需要 个点值来插值就可以得到 的通项 最后就可以得到
现在的问题转换成了如何求 这需要我们再次利用 阶差分是的特性:
复杂度是 我的实现下跑 是当前的最优解 然而这个最优解第二的 跑了(
给个最终的代码吧:
template <typename T>void readb(T &a,T &b){T x = 0,f = 1,y = 0;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') f = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = ((x << 1) + (x << 3) + (ch ^ 48)) % mod;y = ((y << 1) + (y << 3) + (ch ^ 48)) % (mod - 1);ch = getchar();}a = x * f;b = y * f;}struct line{int k,t;line operator+(const line tmp) const{return (line) {(k + tmp.k) % mod,(t + tmp.t) % mod};}line operator-(const line tmp) const{return (line) {(k - tmp.k + mod) % mod,(t - tmp.t + mod) % mod};}line operator*(const int x) const{return (line) {k * x % mod,t * x % mod};}}l[N];int ksm(int a,int b,int mod = mod){int res = 1;while (b){if (b & 1) res = res * a % mod;a = a * a % mod;b >>= 1;}return res;}int C(int n,int m){if (n < m) return 0;if (n < 0 || m < 0) return 0;return fac[n] * inv[n - m] % mod * inv[m] % mod;}int clac(int n){if (n <= k + 1) return g[n];int ans = 0,res = 1;for (int i = 1; i <= k + 1; i++)res = res * (n - i) % mod;for (int i = 1; i <= k + 1; i++){int tmp = inv[i - 1] * inv[k + 1 - i] % mod;ans = (ans + g[i] * tmp % mod * res % mod * ksm(n - i,mod - 2) % mod * ((k + 1 - i) & 1 ? -1 : 1) + mod) % mod;}return ans;}int getf(int n,int _n){return (ksm(a,_n) * clac(n) % mod - g[0] + mod) % mod;}int getsum(int n,int _n){int ans = getf(n,_n);return ans * ksm(2,_n) % mod;}signed main(){a = ksm(2,mod - 2);readb(n,_n);read(k);fac[0] = inv[0] = 1;for (int i = 1; i < N; i++)fac[i] = fac[i - 1] * i % mod;inv[N - 1] = ksm(fac[N - 1],mod - 2);for (int i = N - 1; i >= 1; i--)inv[i - 1] = inv[i] * i % mod;idk[1] = 1;for (int i = 2; i < N; i++){if (!vis[i]) pri[++cnt] = i,idk[i] = ksm(i,k);for (int j = 1; j <= cnt && i * pri[j] < N; j++){vis[i * pri[j]] = 1;idk[i * pri[j]] = idk[i] * idk[pri[j]] % mod;if (!(i % pri[j])) break;}}l[0] = (line) {1,0};for (int i = 1; i <= k + 1; i++)l[i] = l[i - 1] * 2 + (line) {0,idk[i] % mod};line res = (line) {0,0};for (int i = 0; i <= k + 1; i++){if (i & 1) res = res - l[k + 1 - i] * C(k + 1,i);else res = res + l[k + 1 - i] * C(k + 1,i);}g[0] = (mod - res.t * ksm(res.k,mod - 2) % mod) % mod;for (int i = 1; i <= k + 5; i++)g[i] = (2 * g[i - 1] + idk[i]) % mod;writeln((getsum(n,_n) - getsum(n - 1,(_n - 1 + mod - 1) % (mod - 1)) + mod) % mod);return 0;}
8会
看着是可做题
首先考虑题意转换成为一个左边 个点右边 个点 条边的有标号二分图计数 满足所有点的度
而事实上 每行都会有 个棋子 所以:
signed main(){read(n,m);fac[0] = inv[0] = 1;for (int i = 1; i <= n << 1; i++)fac[i] = fac[i - 1] * i % mod;inv[n << 1] = ksm(fac[n << 1],mod - 2);for (int i = n << 1; i >= 1; i--)inv[i - 1] = inv[i] * i % mod;for (int i = 0; i <= n; i++)f[i] = ksm(mod - 1,i) * fac[(n - i) << 1] % mod * C(n,i) % mod * ksm(2,i) % mod;for (int i = 0; i <= n; i++)g[i] = inv[i];lim = 1;while (lim <= n + n) lim <<= 1;getr(lim);ntt(f,1);ntt(g,1);for (int i = 0; i < lim; i++) f[i] = f[i] * g[i] % mod;ntt(f,-1);down[0] = 1;for (int i = 1; i <= n << 1; i++)down[i] = down[i - 1] * ((m - i + 1) % mod) % mod;int ans = 0;for (int i = 0; i <= n; i++)ans = (ans + down[n + n - i] * fac[i] % mod * ksm(ksm(2,n + i),mod - 2) % mod * f[i] % mod * inv[i] % mod * inv[(n - i) << 1] % mod) % mod;writeln(ans);return 0;}
考虑一个 表示到个数 之前选了个数的价值和 有转移:
尝试换一种写法表示前个数中有个不选的价值和:
一个位置的数有种情况:不选() 或者加入一个集合() 或者新开一个集合()
一个数给你乘的贡献相当于这个位置上的数不选
我们不妨拆开贡献 将选个数不选的贡献先求出来 这个的贡献:
我们设:表示个数中按照上述贡献分成个集合的价值
显然有 我们假设 那么
写出这个的EGF:
void cdq(poly &f,int l,int r){if (l == r){f.resize(r - l + 2);f[0] = 1;f[1] = (a[l] + l) % mod;return ;}int mid = (l + r) >> 1;poly A,B;cdq(A,l,mid);cdq(B,mid + 1,r);lim = 1;while (lim <= (r - l + 1)) lim <<= 1;getr(lim);ntt(A,1);ntt(B,1);f.resize(lim);for (int i = 0; i < lim; i++) f[i] = A[i] * B[i] % mod;ntt(f,-1);f.resize(r - l + 2);}signed main(){read(n);for (int i = 1; i <= n; i++)read(a[i]);cdq(f,1,n);fac[0] = inv[0] = 1;for (int i = 1; i <= n; i++)fac[i] = fac[i - 1] * i % mod;inv[n] = ksm(fac[n],mod - 2);for (int i = n; i >= 1; i--)inv[i - 1] = inv[i] * i % mod;for (int i = 1; i <= n; i++)g[i] = mod - inv[i];getexp(g,h,n + 1);for (int i = 0; i <= n; i++)h[i] = h[i] * fac[i] % mod;int ans = 0;for (int i = 0; i <= n; i++){int qaq = n - i;if (qaq & 1) ans = (ans - f[i] * h[qaq] % mod + mod) % mod;else ans = (ans + f[i] * h[qaq] % mod) % mod;}writeln(ans - 1);return 0;}
高质量板子题(?)
signed main(){read(n,k);int w = ksm(3,(mod - 1) / k);int wn = 1,ans = 0;for (int i = 0; i < k; i++){ans = (ans + ksm(wn + 1,n)) % mod;wn = wn * w % mod;}writeln(ans * ksm(k,mod - 2) % mod);return 0;}
套路地考虑对于点对于 的贡献 有这个式子:
void dfs(int u,int f){siz[u] = 1;for (int i = head[u]; i != -1; i = nxt[i]){int v = pnt[i];if (v == f) continue;dfs(v,u);siz[u] += siz[v];cnt[siz[v]]++;}cnt[n - siz[u]]++;}signed main(){memset(head,-1,sizeof(head));memset(cnt,0,sizeof(cnt));read(n);for (int i = 1; i < n; i++){int u,v;read(u,v);add_edge(u,v);add_edge(v,u);}inv[1] = 1;for (int i = 2; i <= n; i++)inv[i] = (mod - mod / i) * inv[mod % i] % mod;fac[0] = ifac[0] = 1;for (int i = 1; i <= n; i++)fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;dfs(1,0);for (int i = 1; i <= n; i++)f[i] = cnt[i] * fac[i] % mod;reverse(f,f + n + 1);for (int i = 0; i <= n; i++)g[i] = ifac[i];lim = 1;while (lim <= (n << 1)) lim <<= 1;getr(lim);ntt(f,1);ntt(g,1);for (int i = 0; i < lim; i++)f[i] = f[i] * g[i] % mod;ntt(f,-1);reverse(f,f + n + 1);for (int i = 1; i <= n; i++)writeln((n * C(n,i) % mod - ifac[i] * f[i] % mod + mod) % mod);return 0;}
好像是个憨憨题诶
考虑计算出 位可以得到的数位的和以及对应的方案数 那么答案就是:
我竟然找到了多项式快速幂的应用(
然后我就头铁写ln+exp的卡了一下午常
没想到吧 exp能跑1e6!甚至不是最慢解
考虑容斥,不合法的答案是有一些数在1之后才被删除
定义全集,以及令
那么我们很容易得到这样一个不合法概率的式子:
而我们知道:
所以我们可以得到:
我们考虑套路地更换枚举顺序:
我们考虑后面地这个东西应该如何计算,不妨考虑他的生成函数:
对于某个数,表示不选这个数,而表示选这个数,那么我们所要的式子就是:
(2)式的处理我们考虑一个分治,并不是分治fft,只是单纯地每次将其分成两部分,再将两部分合并
所以我们要的答案就是:
考虑如何计算和为 的方案数:
signed main(){read(n,m);m++;for (int i = 1; i <= n; i++){int x;read(x);if (x < m) g[x] = mod - 4;}g[0]++;getsqrt(g,h,m);h[0]++;getinv(h,f,m);for (int i = 1; i < m; i++)writeln(2 * f[i] % mod);return 0;}
考虑一个容斥 我们设 表示至少有 个数出现次数小于等于 1 那么:
现在的问题是 个数出现次数小于等于 1 的方案数
我们把这个方案数设出来 设为
对于选出的一个元素 讨论 有两种情况:
假设没有第一种情况 那方案数就是一行斯特林数的和 考虑转化问题 我们认为不出现的元素只是出现在了一个 假定的集合 中 此时我们添加一个标记元素 包含这个元素的集合就是假定集合
那么我们就可以计算 了:
signed main(){read(n,mod);inv[1] = 1;for (int i = 2; i <= n; i++)inv[i] = (mod - mod / i) * inv[mod % i] % mod;fac[0] = ifac[0] = 1;for (int i = 1; i <= n; i++)fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;s[0][0] = 1;for (int i = 1; i <= n + 1; i++)for (int j = 1; j <= i; j++)s[i][j] = (s[i - 1][j - 1] + j * s[i - 1][j] % mod) % mod;int ans = 0;for (int i = 0; i <= n; i++){int res = 0;for (int j = 0; j <= i; j++)res = (res + s[i + 1][j + 1] * ksm(2,j * (n - i)) % mod) % mod;res = res * C(n,i) % mod * ksm(2,ksm(2,n - i,mod - 1)) % mod;if (i & 1) ans = (ans - res + mod) % mod;else ans = (ans + res) % mod;}writeln(ans);return 0;}
考虑一个 的 dp: 表示 轮前 数为 的概率
那么有转移:
总复杂度
signed main(){read(n,m);m %= mod - 1;for (int i = 0; i <= n; i++)read(p[i]);inv[1] = 1;for (int i = 2; i <= n + 1; i++)inv[i] = (mod - mod / i) * inv[mod % i] % mod;fac[0] = ifac[0] = 1;for (int i = 1; i <= n + 1; i++)fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;for (int i = 0; i <= n; i++)f[i] = ifac[i],g[i] = p[n - i] * fac[n - i] % mod;lim = 1;while (lim < (n << 1) + 1) lim <<= 1;getr(lim);ntt(f,1);ntt(g,1);for (int i = 0; i < lim; i++) g[i] = f[i] * g[i] % mod;ntt(g,-1);for (int i = 0; i <= n; i++)g[n - i] = ksm(inv[i + 1],m) * g[n - i] % mod;for (int i = 0; i <= n; i++){if (i & 1) f[i] = mod - ifac[i];else f[i] = ifac[i];}for (int i = n + 1; i < lim; i++)f[i] = g[i] = 0;ntt(f,1);ntt(g,1);for (int i = 0; i < lim; i++) f[i] = f[i] * g[i] % mod;ntt(f,-1);reverse(f,f + n + 1);for (int i = 0; i <= n; i++)writes(f[i] * ifac[i] % mod);puts("");return 0;}
经典题
根据 Polya 我们可以知道:
考虑怎么求 事实上我们好处理的置换是点的置换而非边的置换 所以考虑拆分点的置换
把点的置换 拆分成 这 个的循环
边的两点分成两种:
对于第一种情况 不妨考虑循环长度 对于循环 和 这之间连边的循环长度显然是 那么循环的数量 就是
考虑第二种情况 长度相等的边属于同一个等价类 所以有 种
考虑这样的拆分会对应多少个置换:
那么我们改写我们的最终的式子:
代码:
void clac(int cnt){int res = 0;for (int i = 1; i <= cnt; i++){res = (res + a[i] / 2 * c[i] % (mod - 1)) % (mod - 1);res = (res + c[i] * (c[i] - 1) / 2 % (mod - 1) * a[i] % (mod - 1)) % (mod - 1);for (int j = 1; j < i; j++)res = (res + gcd(a[i],a[j]) * c[i] % (mod - 1) * c[j] % (mod - 1)) % (mod - 1);}res = ksm(m,res);for (int i = 1; i <= cnt; i++)res = res * ksm(inv[a[i]],c[i]) % mod * ifac[c[i]] % mod;ans = (ans + res) % mod;}void dfs(int n,int id,int mini){if (!n) return clac(id - 1);for (int i = mini + 1; i <= n; i++){a[id] = i;c[id] = 0;for (int j = i; j <= n; j += i)c[id]++,dfs(n - j,id + 1,i);}}signed main(){read(n,m,mod);inv[1] = 1;for (int i = 2; i < N; i++)inv[i] = (mod - mod / i) * inv[mod % i] % mod;fac[0] = ifac[0] = 1;for (int i = 1; i < N; i++)fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;dfs(n,1,0);writeln(ans);return 0;}
考虑一个 dp:
表示前 个球取出 组的方案数 有转移:
套路地 设出通项公式为:
我们可以带入解方程 得到:
signed main(){read(n,k);k++;f[0] = 1,f[1] = 6,f[2] = 1;getsqrt(f,delta,k);for (int i = 0; i < k; i++)t1[i] = delta[i] * INV % mod,t2[i] = (mod - delta[i]) * INV % mod,f[i] = 0;t1[0] = (t1[0] + INV) % mod,t1[1] = (t1[1] + INV) % mod;t2[0] = (t2[0] + INV) % mod,t2[1] = (t2[1] + INV) % mod;getksm(t1,f,k,n + 1,n + 1,(int) (log(n + 1) / log(10)));getksm(t2,g,k,n + 1,n + 1,(int) (log(n + 1) / log(10)));getinv(delta,h,k);for (int i = 0; i < k; i++)f[i] = (f[i] + g[i]) % mod;lim = 1;while (lim < (k << 1) - 1) lim <<= 1;getr(lim);ntt(f,1);ntt(h,1);for (int i = 0; i < lim; i++) f[i] = f[i] * h[i] % mod;ntt(f,-1);for (int i = 1; i < k && i <= n; i++)writes(f[i]);for (int i = n + 1; i < k; i++)putchar('0'),putchar(' ');puts("");return 0;}
确认过 是个套路题
首先有一个很显然的 dp : 表示前 个数 最后一个数是 的方案数
显然不行的是 值域很大 但是 很小 所以可以考虑离散化后对于一个左闭右开区间进行 dp
考虑 表示前 个数 最后那个数在 区间的方案数
转移时考虑枚举在 区间内的数的个数 然后统计答案
signed main(){read(n);for (int i = 1; i <= n; i++){read(l[i],r[i]);r[i]++;id[++cnt] = l[i];id[++cnt] = r[i];}sort(id + 1,id + cnt + 1);cnt = unique(id + 1,id + cnt + 1) - id - 1;for (int i = 1; i <= n; i++){l[i] = lower_bound(id + 1,id + cnt + 1,l[i]) - id;r[i] = lower_bound(id + 1,id + cnt + 1,r[i]) - id;}for (int i = 1; i <= cnt; i++) dp[0][i] = 1;for (int i = 1; i <= n; i++){for (int j = l[i]; j < r[i]; j++){int len = id[j + 1] - id[j];for (int k = i; k >= 1; k--){if (j < l[k] || r[k] <= j) break;dp[i][j] = (dp[i][j] + dp[k - 1][j + 1] * C(i - k + len,i - k + 1) % mod) % mod;}}for (int j = cnt - 1; j >= 1; j--)dp[i][j] = (dp[i][j] + dp[i][j + 1]) % mod;}for (int i = 1; i <= n; i++)dp[n][1] = dp[n][1] * ksm(id[r[i]] - id[l[i]],mod - 2) % mod;writeln(dp[n][1]);return 0;}
考虑: 表示 恰好有 行 列同色的方案数 表示 至少有 行 列同色的方案数
那么显然有:
signed main(){read(n);inv[1] = 1;for (int i = 2; i <= n; i++)inv[i] = (mod - mod / i) * inv[mod % i] % mod;fac[0] = ifac[0] = 1;for (int i = 1; i <= n; i++)fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;int ans = 0;for (int i = 1; i <= n; i++){if (i & 1) ans = (ans - C(n,i) * ksm(ksm(3,(i * n) % (mod - 1)),mod - 2) % mod * (ksm(1 - ksm(ksm(3,n - i),mod - 2) + mod,n) - 1) % mod + mod) % mod;else ans = (ans + C(n,i) * ksm(ksm(3,(i * n) % (mod - 1)),mod - 2) % mod * (ksm(1 - ksm(ksm(3,n - i),mod - 2) + mod,n) - 1) % mod) % mod;}ans = ans * ksm(3,(n * n + 1) % (mod - 1));for (int i = 1; i <= n; i++){if (i & 1) ans = (ans - 2 * C(n,i) * ksm(3,(n * (n - i) + i) % (mod - 1)) + mod) % mod;else ans = (ans + 2 * C(n,i) * ksm(3,(n * (n - i) + i) % (mod - 1))) % mod;}writeln((mod - ans) % mod);return 0;}
首先考虑圆的面积并怎么做 考虑格林公式:
const long double eps = 1e-11,pi = acos(-1.0);int n,vis[N];long double sqr(long double x){return x * x;}struct point{long double x,y;bool operator<(const point &tmp) const{if (x != tmp.x) return x + eps < tmp.x;return y + eps < tmp.y;}bool operator==(const point &tmp) const{return fabs(x - tmp.x) <= eps && fabs(y - tmp.y) <= eps;}bool operator!=(const point &tmp) const{return !(fabs(x - tmp.x) <= eps && fabs(y - tmp.y) <= eps);}point operator+(const point &tmp) const{return (point) {x + tmp.x,y + tmp.y};}point operator-(const point &tmp) const{return (point) {x - tmp.x,y - tmp.y};}long double angle(){return atan2(y,x);}};struct cir{point o;long double r;bool operator<(const cir &tmp) const{if (o != tmp.o) return o < tmp.o;return r < tmp.r;}bool operator==(const cir &tmp) const{return o == tmp.o && fabs(r - tmp.r) <= eps;}long double oint(long double phi2,long double phi1){return (r * (phi1 - phi2) + o.x * (sin(phi1) - sin(phi2)) - o.y * (cos(phi1) - cos(phi2))) * r;}}a[N];struct node{long double l,r;bool operator<(const node &tmp) const{return r + eps < tmp.r;}};set<node> s[N];bool in(cir x,cir y){point tmp = x.o - y.o;return sqr(tmp.x) + sqr(tmp.y) <= sqr(y.r - x.r);}bool out(cir x,cir y){point tmp = x.o - y.o;return sqr(tmp.x) + sqr(tmp.y) >= sqr(x.r + y.r);}long double update(int id,long double l,long double r){long double res = 0;set<node>::iterator it2;for (set<node>::iterator it = s[id].lower_bound((node) {0,l}); it != s[id].end() && it -> l < r; it = it2){long double nowl = it -> l,nowr = it -> r;it2 = it;it2++;s[id].erase(it);res -= a[id].oint(nowl,nowr);if (nowl < l) res += a[id].oint(nowl,l),s[id].insert((node) {nowl,l});if (nowr > r) res += a[id].oint(r,nowr),s[id].insert((node) {r,nowr});}return res;}signed main(){read(n,n);for (int i = 1; i <= n; i++)scanf("%Lf%Lf%Lf",&a[i].o.x,&a[i].o.y,&a[i].r);long double ans = 0;for (int i = 1; i <= n; i++){vis[i] = 1;for (int j = 1; j < i; j++){if (in(a[i],a[j]) && a[j].r >= a[i].r){vis[i] = 0;break;}if (in(a[j],a[i]) && a[i].r >= a[j].r){vis[j] = 0;ans += update(j,-pi,pi);}}if (!vis[i]){printf("%.10Lf\n",ans * 0.5);continue;}ans += a[i].oint(-pi,pi);s[i].insert((node) {-pi,pi});for (int j = 1; j < i; j++){if (!vis[j] || out(a[i],a[j])) continue;point qaq = a[i].o - a[j].o;long double dis = sqrt(sqr(qaq.x) + sqr(qaq.y)),alpha = qaq.angle();long double beta = acos((sqr(dis) + sqr(a[j].r) - sqr(a[i].r)) / (2.0 * dis * a[j].r));long double l = alpha - beta,r = alpha + beta;if (l < -pi) l += 2.0 * pi;if (l > pi) l -= 2.0 * pi;if (r < -pi) r += 2.0 * pi;if (r > pi) r -= 2.0 * pi;if (l < r) ans += update(j,l,r);else ans += update(j,-pi,r),ans += update(j,l,pi);qaq = a[j].o - a[i].o;alpha = qaq.angle();beta = acos((sqr(dis) + sqr(a[i].r) - sqr(a[j].r)) / (2.0 * dis * a[i].r));l = alpha - beta,r = alpha + beta;if (l < -pi) l += 2.0 * pi;if (l > pi) l -= 2.0 * pi;if (r < -pi) r += 2.0 * pi;if (r > pi) r -= 2.0 * pi;if (l < r) ans += update(i,l,r);else ans += update(i,-pi,r),ans += update(i,l,pi);}printf("%.10Lf\n",ans * 0.5);}return 0;}
你怎么写个题面还写同位语从句的啊
首先套路地破环为链 考虑序列上 那么染色的方式可以大致分成四种考虑:
考虑 dp:
把上面那堆全部写成生成函数的形式:
于是我就偷懒直接 ntt 了
int f[N << 2] = {0,0,0,24,4,144,mod - 4,348,mod - 128,240,28,188,mod - 68,16,0,4,0};int g[N << 2] = {1,0,mod - 4,mod - 8,1,mod - 16,10,mod - 4,12,48,mod - 26,44,mod - 15,16,4,4,1};signed main(){read(n);n++;getinv(g,h,n);lim = 1;while (lim < (n << 1)) lim <<= 1;getr(lim);for (int i = n; i < lim; i++)f[i] = h[i] = 0;ntt(h,1);ntt(f,1);for (int i = 0; i < lim; i++) h[i] = h[i] * f[i] % mod;ntt(h,-1);writeln(h[n - 1]);return 0;}
u1s1 whk 好玩
题意写的太复杂了 简单来说就是:
给你一个随机数生成器 等概率地生成一个 的整数
你生成 次 问你生成的数的和在 的概率
我们知道一个满足正态分布的随机变量 的概率密度函数:
但是题目中的并不是正态分布 由中心极限定理可以知道:
于是我们事实上就是求
小数据大力 就可以了
namespace sub1{double f[N];signed main(int x,int y){f[0] = pow(1.0 / x,y);for (int i = 1; i <= x * y - 1; i++){f[i] = 0;for (int j = 1; j <= min(x - 1,i); j++)f[i] += y * j * f[i - j] - (i - j) * f[i - j];f[i] /= i;}for (int i = 1; i <= x * y - 1; i++)f[i] += f[i - 1];for (int t = 1; t <= 10; t++){int l,r;read(l,r);printf("%.7lf\n",f[r] - (l ? f[l - 1] : 0));}return 0;}}namespace sub2{const double qaq = sqrt(2);signed main(int x,int y){double mu = (x - 1) / 2.0 * y,sig = sqrt((x * x - 1) / 12.0 * y);for (int t = 1; t <= 10; t++){int l,r;read(l,r);printf("%.7lf\n",(erf((r - mu) / sig / qaq) - erf((l - 1 - mu) / sig / qaq)) / 2);}return 0;}}signed main(){int t;read(t);while (t--){int x,y;read(x,y);if (x * y <= 2000) sub1::main(x,y);else sub2::main(x,y);}return 0;}
考虑什么时候会重复计算方案 当且仅当 和
我们考虑去掉这种方案 我们统计不存在 的方案
考虑容斥或者二项式反演 枚举满足上条件的行列数 快进到最后的式子:
signed main(){inv[1] = 1;for (int i = 2; i < N; i++)inv[i] = (mod - mod / i) * inv[mod % i] % mod;fac[0] = ifac[0] = 1;for (int i = 1; i < N; i++)fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;read(n,m);int ans = 0,res = 0;for (int i = 0; i <= n && i <= m; i++){if (i & 1) ans = (ans - C(n,i) * C(m,i) % mod * fac[i] % mod * ksm(n + 1, m - i) % mod * ksm(m + 1,n - i) % mod + mod) % mod;else ans = (ans + C(n,i) * C(m,i) % mod * fac[i] % mod * ksm(n + 1, m - i) % mod * ksm(m + 1,n - i) % mod) % mod;}writeln(ans);return 0;}
直接考虑容斥 枚举没有被染色的边的数量 考虑把边断开之后的每一棵子树分开染色 一棵大小为 的树的方案:
给个实现:
void dfs(int u,int fa){siz[u] = dp[u][1] = 1;for (int i = head[u]; i != -1; i = nxt[i]){int v = pnt[i];if (v == fa) continue;dfs(v,u);for (int j = 1; j <= siz[u] + siz[v]; j++) g[j] = 0;for (int j = siz[u]; j >= 1; j--)for (int k = siz[v]; k >= 0; k--)g[j + k] = (g[j + k] + dp[u][j] * dp[v][k] % mod) % mod;for (int j = 1; j <= siz[u] + siz[v]; j++) dp[u][j] = g[j];siz[u] += siz[v];}for (int i = 2; i <= siz[u]; i += 2)dp[u][0] = (dp[u][0] - dp[u][i] * f[i] % mod + mod) % mod;}signed main(){memset(head,-1,sizeof(head));read(n);for (int i = 1; i < n; i++){int u,v;read(u,v);add_edge(u,v);add_edge(v,u);}f[0] = 1;for (int i = 2; i <= n; i += 2)f[i] = f[i - 2] * (i - 1) % mod;dfs(1,0);writeln(mod - dp[1][0]);return 0;}