@computerkiller
2021-02-18T20:45:53.000000Z
字数 27170
阅读 476
数学
题解
改了标题之后里面的题目就杂起来了
那么问题来了 计算几何算不算数学题
套路题
考虑某个不为的物品的生成函数:
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;
}