4.8日下午练习

4.8日下午练习

T1 owo

首先 50 分很好说就不详细说了。

我们可以发现我们要求的其实就是 \(\displaystyle\sum_{i=1}^{m}\frac{x}{x+b[i]}\)

首先因为精度的要求不是很严格所以我们可以采用类似于随机撒点的方法进行判断,但是很可惜不能通过本题。那么就想到了一种收敛的想法。利用这个性质来做这个题。

具体来说我们把上面的 \(x\) 项先提出来。这样我们剩下的就是 \(\displaystyle\sum_{i=1}^m\frac{1}{x+b[i]}\)

然后我们假设 \(X=x+1.5,Y=1 .5-b[i]\) ,这样的话由于 \(b\) 的范围是在 1-2 之间的。所以利用 1.5 去减的话,最后剩下的就是 \([-0.5,0.5]\) 之间的数,那么就基本满足了我们的收敛形式。

此时我们的分母就变成了 \(X-Y\),那么我们考虑构造使得下面的那个式子可以消掉。

\[\frac{X^k-Y^k}{X-Y}=\sum_{i=0}^{k-1}X^iY^{k-1-i} \]

由于在 n 次方下, \(Y^n\) 的贡献是可以忽略的。因此。

\[\frac{X^k}{X-Y}=\sum_{i=0}^{k-1}X^iY^{k-1-i} \\ \frac{1}{X-Y}=\sum_{i=0}^{k-1} \frac{X^iY^{k-1-i}}{X^k} \\ \frac{1}{X-Y}=\sum_{i=0}^{k-1} \frac{Y^{k-i-1}}{X^{k-i}} \\ \frac{1}{X-Y}=\sum_{i=1}^{k} \frac{Y^{i-1}}{X^i} \]

然后上面的那个 \(Y\) 其实是可以进行预处理的,所以这样的话,我们就可以 \(O(kN)\) 的复杂度解决这个问题。其实 \(k\) 取到 30-40 应该就可以了。

//editor : DRYAYST
//Wo shi ge da SHA BI
#include<bits/stdc++.h>
#define g() getchar()
#define il inline
#define ull unsigned long long
#define eps 1e-10
#define ll long long
#define pa pair<int, int>
#define for_1(i, n) for(int i = 1; i <= (n); ++i)
#define for_0(i, n) for(int i = 0; i < (n); ++i)
#define for_xy(i, x, y) for(int i = (x); i <= (y); ++i)
#define for_yx(i, y, x) for(int i = (y); i >= (x); --i)
#define for_edge(i, x) for(int i = head[x]; i; i = nxt[i])
#define int long long
#define DB double
#define ls (p<<1)
#define rs (p<<1|1)
#define m_p make_pair
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 10, INF = 0x7f7f7f7f, mod = 1e9 + 7;
il int qpow(int x, int k) {int ans = 1; while(k) {if(k & 1) ans = ans * x % mod; x = x * x % mod; k >>= 1; } return ans; }
il int Add(int x, int y) {return (x += y) %= mod;}
il int Del(int x, int y) {return (x = x - y + mod) % mod;}
il int Mul(int x, int y) {return x * y % mod;}
il int inv(int x) {return qpow(x, mod - 2); }
inline int re() {
    int x = 0, p = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0') {if(ch == '-') p = -1; ch = getchar();}
    while(ch <= '9' and ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    return x * p;
}

int n, m; 
DB a[N], b[N], sum[N], ans[N]; 

signed main() { 
     freopen("owo.in","r",stdin); freopen("owo.out","w",stdout); 
    n = re(), m = re(); for_1(i, n) scanf("%lf", &a[i]); for_1(i, m) scanf("%lf", &b[i]); 
    for(int i = 1; i <= m; ++i) {
        DB t = 1.5 - b[i], res = 1.0; 
        for(int j = 1; j <= 40; ++j) { sum[j] += res;  res *= t;  }
    }
    for(int i = 1; i <= n; ++i) {
        DB X = a[i] + 1.5, res = X; 
        for(int j = 1; j <= 40; ++j) {
            ans[i] += sum[j] / res; res *= X; 
        }
        ans[i] = ans[i] * a[i];
    }
    for_1(i, n) printf("%.6f\n", ans[i]); 
}
/*
3 2
1.0 2.0 1.5
1.0 2.0
*/

T2

怎么说呢,这种类似的题也见了不少了。主要这种组合计数都是应用了一种 dp 的思路吧,进行容斥得到最后的答案。

首先考虑比较简单的情况,也就是维度比较少的情况。这样我们可以对于每一个格子进行考虑。

\(f[i]\) 表示走了 \(i\) 步之后第一次达到当前枚举的格子的方案数。

\(g[i]\) 表示走了 \(i\) 步之后在这个格子的方案数。

\(h[i]\) 表示从当前格子出发出发走 \(i\) 步再次回到当前的格子的方案数。

那么现在求 \(f\) 的话就是一个很简单的容斥了。

\(f[i]=g[i]-\sum_{j=1}^{i}f[j]*h[i-j]\)

简单来说就是从 \(i\) 步走到这个点的方案数里面找出来第一次到达的方案,然后利用一个容斥进行计算即可。

这样的话我们直接枚举所有的点,对于所有的格子统计一下答案即可。

但是对于 \(k\) 维数更多的就不是很好做了。接着考虑上面的递推式。

\(h[i]\) 可以单独进行计算。可以发现各个维度是互不影响的,所以可以进行背包转移。然后 \(h\) 就可以算出来了。

那么本质上就是 \(g\)\(f\) 之间的线性变换。

所以我们不去枚举所有的格子,我们直接对于所有的格子进行求和。这样一来我们就可以统计答案了。

\(f[i]=(2k)^i-\sum_{j=1}^{i}f[j]*h[i-j]\)

直接 dp 即可。

//editor : DRYAYST
//Wo shi ge da SHA BI
#include<bits/stdc++.h>
#define g() getchar()
#define il inline
#define ull unsigned long long
#define eps 1e-10
#define ll long long
#define pa pair<int, int>
#define for_1(i, n) for(int i = 1; i <= (n); ++i)
#define for_0(i, n) for(int i = 0; i < (n); ++i)
#define for_xy(i, x, y) for(int i = (x); i <= (y); ++i)
#define for_yx(i, y, x) for(int i = (y); i >= (x); --i)
#define for_edge(i, x) for(int i = head[x]; i; i = nxt[i])
#define int long long
#define DB double
#define ls (p<<1)
#define rs (p<<1|1)
#define m_p make_pair
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 10, INF = 0x7f7f7f7f;
inline int re() {
    int x = 0, p = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0') {if(ch == '-') p = -1; ch = getchar();}
    while(ch <= '9' and ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    return x * p;
}

int C[3000][3000], pw[N], h[3000][20], dp[N]; 
int n, m, mod, ans; 

signed main() {
     freopen("qaq.in","r",stdin); freopen("qaq.out","w",stdout); 
    n = re(), m = re(), mod = re();  pw[0] = 1; dp[0] = 1;   C[0][0] = 1; 
    for(int i = 1; i <= 2500; ++i) {
        C[i][0] = C[i][i] = 1; 
        for(int j = 1; j <= i; ++j) { C[i][j] = (C[i-1][j-1] + C[i-1][j]) % mod; }
    }
    for(int i = 0; i <= n / 2; ++i) h[i][1] = C[i * 2][i]; 
    for(int k = 2; k <= m; ++k) 
        for(int i = 0; i <= n / 2; ++i)
            for(int j = 0; j <= i; ++j) 
                h[i][k] = (h[i][k] + h[j][k-1] * h[i -j][1] % mod * C[2 * i][2 * j] % mod) % mod; 
    for(int i = 1; i <= n; ++i) {
        pw[i] = pw[i - 1] * (2 * m) % mod; dp[i] = pw[i]; 
        for(int j = 2; j <= i; j += 2) {dp[i] = (dp[i] - dp[i - j] * h[j / 2][m] % mod + mod) % mod; }
    }
    for(int i = 0; i <= n; ++i) ans = (ans + dp[i] * pw[n - i] % mod) % mod; 
    cout<<(ans+mod)%mod<<endl;  
}
/*
4 2 123456789

30 2 1000000000

50 5 876543210
*/

T3

比较简单,但是不是很好写。首先路径求交很简单。

那么接下来分成两种情况。

  1. 方向相反,那么我们直接判断是否在端点处相遇即可
  2. 方向相同,考虑路径上的最长的长度和时间差做差即可

嗯哼?严厉谴责 @Eternal_Battle 的 D 人行为。/xyx

posted @ 2022-04-08 21:34  Zwaire  阅读(54)  评论(3)    收藏  举报