2018 Multi-University Training Contest 6

传送门

A - oval-and-rectangle

数学题,积分+换元即可。

Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ld;
const int MAXN = 1e3 + 5, MAXM = 5e6 + 5, BOUND = 2e5, MOD = 998244353, INF = 0x3f3f3f3f, base = 10000;
const int inv2 = (MOD + 1) >> 1;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define lc(x) ch[x][0]
#define pii pair<int,int>
#define vi vector<int>
#define vii vector<pair<int,int>>
#define rc(x) ch[x][1]
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define all(a) (a).begin(), (a).end()
#define sz(a) int(a.size())
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fi first
#define se second
#define MP std::make_pair
#define ri register int
//#define sz(a) int((a).size())
const int N = 2e5,M = (1<<20);
inline int add(int a, int b) {return a + b >= MOD ? a + b - MOD : a + b;}
inline int dec(int a, int b) {return a < b ? a - b + MOD : a - b;}
inline int mul(int a, int b) {return 1ll * a * b % MOD;}
template <typename T>
inline void cmin(T &a,T b){a = min(a,b);}
template <typename T>
inline void cmax(T &a,T b){a = max(a,b);}
ll qpow(ll a,ll b){
    ll ans=1;
    for(;b;b>>=1,a=a*a%MOD)if(b&1)ans=ans*a%MOD;
    return ans;
}
mt19937 mrand(random_device{}());

void run(){
    db a,b;cin>>a>>b;
    printf("%.6f
",2*b+PI*a-0.0000005);
}
int main() {
    //ios::sync_with_stdio(false); cin.tie(0);
    int _; scanf("%d",&_);
    while(_--)run();
    return 0;
}

B - bookshelf

题意:
(n)个小球放入(k)个盒子中,(n,kleq 10^6)
假设第(i)个盒子有(cnt_i)个小球,那么(val_i=2^{fib[cnt_i]}-1)
最终总的贡献为(displaystyle gcd(val_1,val_2,...,val_k))
求最终总贡献的期望值。

思路:
介绍两个等式,其中一个为:

[gcd(2^x-1,2^y-1)=2^{gcd(x,y)}-1 ]

证明如下:
不妨(x<y),那么(displaystyle gcd(2^x-1,2^y-1)=gcd(2^x-1,2^y-2^x)=gcd(2^x-1,2^xcdot (2^{y-x}-1)))
(d=gcd(2^x-1,2^y-1)),显然(d)为奇数,也就是说(d ot {|}2^x),那么就有(displaystyle gcd(2^x-1,2^xcdot (2^{y-x}-1))=gcd(2^x-1,2^{y-x}-1))
也就是说我们可以对指数运用欧几里得算法,那么就有(displaystyle gcd(2^x-1,2^y-1)=gcd(2^0-1,2^{gcd(x,y)}-1)=2^{gcd(x,y)}-1)
故得证。
然后还有第二个式子:

[gcd(fib[a],fib[b])=fib[gcd(a,b)] ]

证明如下:
首先有个引理:

[f_{m+n}=f_{m}f_{n+1}+f_{m-1}f_{n} ]

证明就是构造个矩阵:

[A^n=left( egin{array}{cc} f_{n-1}&f_n\ f_n &f_{n-1} end{array} ight) ]

然后通过(displaystyle A^{m+n}=A^m imes A^n)可得。
还有一个引理:

  • (m,ngeq 1),那么(f_m|f_{nm})

证明的话我们可以对(n)做归纳来证明,证明的话要用到我们刚刚提到的引理。

然后就开始推式子:
(n=mk+r),有:

[egin{aligned} gcd(f_m,f_n)=&gcd(f_n,f_{mk+1}f_r+f_{mk}f_{r-1})\ =&gcd(f_m,f_{mk+1}f_r)\ =&gcd(f_m,f_r) end{aligned} ]

我们可以发现下标也可以运用欧几里得算法,所以(displaystyle gcd(f_m,f_n)=f_{gcd(m,n)})
还有个推论:

  • (f_m|f_n),则(m|n)

证明的话就运用(f_m=gcd(f_m,f_n)=f_{gcd(m,n)}=f_m)

以上相关证明详见传送门

好了,回到这个题,我们最后就是要求:

[2^{f_{gcd(cnt_1,cnt_2,...,cnt_k)}}-1 ]

因为我们最终是求期望,那么我们只需要对每一个(displaystyle d=gcd(cnt_1,cnt_2,...,cnt_k)),求出这个的方案数即可。
这就是老套路了:

[egin{aligned} &sum_{c_1=0}^nsum_{c_2=0}^ncdotssum_{c_k=0}^n[gcd(c_1,c_2,cdots,c_k)=d]\ =&sum_{c_1=0}^{frac{n}{d}}sum_{c_2=0}^{frac{n}{d}}cdotssum_{c_k=0}^{frac{n}{d}}[gcd(c_1,c_2,cdots,c_k)=1]\ =&sum_{c_1=0}^{frac{n}{d}}sum_{c_2=0}^{frac{n}{d}}cdotssum_{c_k=0}^{frac{n}{d}}sum_{t|c_1,cdots,t|c_k}mu(t)\ =&sum_{t=1}^nmu(t)sum_{c_1=0}^{frac{n}{dt}}sum_{c_2=0}^{frac{n}{dt}}cdotssum_{c_k=0}^{frac{n}{dt}} end{aligned} ]

一顿推导猛如虎,后来发现忽视了(cnt_1+cnt_2+cdots+cnt_k=n)这个条件。
我们发现,若(gcd(cnt_1,cnt_2,cdots,cnt_k)=d),那么必然有(d|n),那么就是(displaystyle dt_1+dt_2+cdots+dt_k=n)(displaystyle t_1+t_2+cdots+t_k=frac{n}{d},t_igeq 0)
其实这就是隔板法的一个经典应用,假设我们算出了方案数(F(d)),发现这其实是(gcd)(d)的倍数的方案数。
那么我们要算(f(d))的话,只需要减去(f(2d)+f(3d)+cdots+f(td))就行。就是个简单容斥。
当然,我们也可以借助莫比乌斯函数:(F(d)=F(d)-F(2d)-F(3d)-F(5d)+F(6d)...)这样来算。
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/5/15 20:46:04
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e6 + 5, MOD = 1e9 + 7;

int qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;   
}

int fib[N], fac[N], inv[N];
void init() {
    fib[0] = 0, fib[1] = 1;
    for (int i = 2; i < N; i++) {
        fib[i] = (fib[i - 1] + fib[i - 2]) % (MOD - 1);   
    }
    fac[0] = 1;
    for (int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qpow(fac[N - 1], MOD - 2);
    for (int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}
int C(int n, int m) {
    return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;   
}

int n, k;
int f[N];

void run() {
    cin >> n >> k;
    int tot = C(n + k - 1, k - 1);
    int ans = 0;
    vector <int> d;
    for (int i = 1; i <= n; i++) {
        if (n % i == 0) {
            d.push_back(i);
            f[i] = C(n / i + k - 1, k - 1);
        }
    }
    for (int i = sz(d) - 1; i >= 0; i--) {
        for (int j = i + 1; j < sz(d); j++) {
            if (d[j] % d[i] == 0) {
                f[d[i]] = (f[d[i]] - f[d[j]] + MOD) % MOD;
            }
        }
    }
    for (auto it : d) {
        ans = (ans + 1ll * f[it] * (qpow(2, fib[it] % (MOD - 1)) - 1 + MOD) % MOD) % MOD;
    }
    ans = 1ll * ans * qpow(tot, MOD - 2) % MOD;
    cout << ans << '
';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    init();
    int T; cin >> T; while(T--)
    run();
    return 0;
}

C - Ringland

题意:
给定一个长度为(L),现有(2n)个位置,有(n)个新郎,(n)个新娘。
现在每个新郎要找到一个新娘,会花费一定的代价,代价为到新娘位置最短的距离。
问配对最小的代价之和为多少。

思路:
题意可以抽象为给出一个括号序列,现在要将(n)个左括号和(n)个右括号进行匹配,匹配代价为距离。问最终匹配代价的和最小为多少。
注意到有这样一个事实:

  • 假设最优方案中,(i,j)相匹配,那么(i)之后的左括号必然和(j)之后的右括号匹配(注意这是个循环序列,我们可以拼接一份在后面)。

因为如果不是这样,我们必然可以通过交换匹配使得答案更优。
简单来说,我们确定一对匹配之后,后面的依次也就确定了,比如((1,3),(2,4),(3,5),cdots)
之后考虑枚举断点将环拆开,考虑一个括号序列的最优答案。一个序列中的最优答案即是((1,1),(2,2),cdots,(n,n))这样来匹配,其中((i,j))表示第(i)个左括号,第(j)个右括号。如果不是这样的话比如是((1,2),(2,3)...)来匹配的话我们在另外一个序列中能够枚举到。最后所有这样的最优答案取(min)就是最终的最优答案。
接下来我们通过考虑每个位置的贡献来计算答案。我们将(()看作(1),将())看作(-1),序列前缀和为(s_i)。那么:

  • 对于(()而言,(s_i>0)贡献为负,(s_ileq 0)贡献为正;
  • 对于())而言,(s_igeq 0)贡献为正,(s_i<0)贡献为负。

可以根据这一点对每个位置单独计算,能够很方便地统计信息。
序列每移动一位,所有的前缀和值都会变化不超过(1),这里可以用一个偏移量来统计。
用树状数组来写的话会方便很多。
细节见代码吧,思路真是挺巧妙的,主要在于匹配方式这一点,然后通过这一点我们可以枚举断点来统计。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/5/21 11:20:52
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;

namespace IO{ 
    #define BUF_SIZE 100000 
    #define OUT_SIZE 100000 
    bool IOerror=0; 
    inline char nc(){ 
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE; 
        if (p1==pend){ 
            p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin); 
            if (pend==p1){IOerror=1;return -1;} 
        } 
        return *p1++; 
    } 
    inline bool blank(char ch){return ch==' '||ch=='
'||ch=='
'||ch=='	';} 
    inline void read(int &x){ 
        bool sign=0; char ch=nc(); x=0; 
        for (;blank(ch);ch=nc()); 
        if (IOerror)return; 
        if (ch=='-')sign=1,ch=nc(); 
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0'; 
        if (sign)x=-x; 
    } 
    struct Ostream_fwrite{ 
        char *buf,*p1,*pend; 
        Ostream_fwrite(){buf=new char[BUF_SIZE];p1=buf;pend=buf+BUF_SIZE;} 
        void out(char ch){ 
            if (p1==pend){ 
                fwrite(buf,1,BUF_SIZE,stdout);p1=buf; 
            } 
            *p1++=ch; 
        } 
        void println(ll x){ 
            static char s[25],*s1;s1=s; 
            if (!x)*s1++='0';if (x<0)out('-'),x=-x; 
            while(x)*s1++=x%10+'0',x/=10; 
            while(s1--!=s)out(*s1); out('
'); 
        } 
        void flush(){if (p1!=buf){fwrite(buf,1,p1-buf,stdout);p1=buf;}} 
        ~Ostream_fwrite(){flush();} 
    }Ostream; 
    inline void println(ll x){Ostream.println(x);} 
    inline void println(){Ostream.out('
');} 
    inline void flush(){Ostream.flush();}
};

int n, L;
int sum[N];
pii a[N];

struct BIT {
    ll c[N];
    void init() {
        memset(c, 0, sizeof(ll) * (2 * n + 4));
    }
    int lowbit(int x) {
        return x & -x;
    }
    void add(int x, int v) {
        x += n + 1;
        for (; x < 2 * n + 4; x += lowbit(x)) {
            c[x] += v;
        }
    } 
    ll query(int x) {
        x += n + 1;
        ll res = 0;
        for (; x; x -= lowbit(x)) {
            res += c[x];
        }
        return res;
    }
}bit;

void run() {
    IO::read(n), IO::read(L);
    for (int i = 1; i <= 2 * n; i++) {
        IO::read(a[i].fi);
        a[i].se = (i <= n ? 1 : -1);
    }
    sort(a + 1, a + 2 * n + 1);
    ll tot = 0;
    for (int i = 1; i <= 2 * n; i++) {
        sum[i] = sum[i - 1] + a[i].se;
    }
    bit.init();
    for (int i = 1; i <= 2 * n; i++) {
        if (a[i].se == 1) {
            bit.add(sum[i], 2 * a[i].fi), tot -= a[i].fi;
        } else {
            bit.add(sum[i] + 1, -2 * a[i].fi), tot += a[i].fi;
        }
    }
    ll ans = tot + bit.query(0);
    for (int i = 1; i < 2 * n; i++) {
        if (a[i].se == 1) {
            bit.add(sum[i], 2 * L), tot -= L;
        } else {
            bit.add(sum[i] + 1, -2 * L), tot += L;
        }
        ans = min(ans, tot + bit.query(sum[i]));
    }
    printf("%lld
", ans);
}

int main() {
    int T; scanf("%d", &T); while(T--)
    run();
    return 0;
}

I - Werewolf

题意:
现有狼和村民两种阵营,狼可说真话可说假话,村民只会说真话。每个人只会说一句话。
现在问有多少人能够确定是狼和村民。

思路:
显然能够被确定为村民的人为(0),因为狼可以被当作村民。
接下来就考虑只能被确定为狼的有哪些,也就是一个人如果有至少一种可能为村民那他就不满足条件。
这里我们只需要推理一下即可。
首先连接所有的村民边,如果形成一个环,那么他们都可以为村民,就不可能存在铁狼。
那么就会形成一个森林,接下来考虑狼边。

  • 如果狼边连向其它连通块,那么该连通块中可以所有都为村民,另外一个连通块全为狼。
  • 如果连向自己连通块中的结点(i),那么根节点到(i)都只能是铁狼,否则会出现矛盾。
  • 如果有其它狼边连向该连通块,那么该连通块可能都为村民也可能都为狼。

综上我们会发现,只有第二种情况会出现铁狼,我们就找满足第二种条件的点即可。
就是简单推理推理?虽然说起来很简单,但是从头想的时候还是没那么容易的。。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/5/15 14:59:03
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;

int n;
vector <int> rG[N];
vector <pii> edges;
int f[N];

int find(int x) {
    return f[x] == x ? f[x] : f[x] = find(f[x]);
}
void merge(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx != fy) f[fx] = fy;
}

int rdfs(int u) {
    int res = 1;
    for (auto v : rG[u]) {
        res += rdfs(v);
    }
    return res;
}

void run() {
    cin >> n;
    edges.clear();
    for (int i = 1; i <= n; i++) rG[i].clear();
    for (int i = 1; i <= n; i++) f[i] = i;
    for (int i = 1; i <= n; i++) {
        int x; string s;
        cin >> x >> s;
        if (s[0] == 'v') {
            merge(i, x);
            rG[x].push_back(i);
        } else {
            edges.push_back(MP(i, x));
        }
    }
    int ans1 = 0, ans2 = 0;
    for (auto edge : edges) {
        int u = edge.fi, v = edge.se;
        if (find(u) == find(v)) {
            ans2 += rdfs(v);
        }
    }
    cout << ans1 << ' ' << ans2 << '
';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

K - sacul

题意:
定义海绵宝宝矩阵:(HMBB_i)的大小为(p^icdot p^i),其中(p)为一个质数。矩阵元素为(HMBB_n=({ichoose j}mod p>0)?1:0)。并且(HMBB_n^k)为矩阵的(k)次方。
定义(displaystyle F[n][k]=sum HMBB_n^k),即矩阵的元素之和。

[sum_{i=1}^nsum_{j=1}^kF[i][j] ]

思路:
看到这种奇奇怪怪一大堆的条件,一般就是把看似复杂、冗余的条件变得简单,就像B题一样。
我们注意到这是个(01)矩阵,那么每个位置上的数可以表示为“可达关系”,(k)次方就是走(k)步之后的可达关系。
大组合数取模相关的问题我们一般可以联想到lucas定理,这个题的题面反过来也是lucas定理。lucas定理其实就是将组合数的两个参数拆成(p)进制每位来求,如果最后大于(0)的话也就是对于({nchoose m})来说(ngeq m),不会出现(n<m)的情况。
如果走(k)步之后还为(1),说明矩阵中相应(k)个数值都满足上述情况。也就是(displaystyle{ichoose j}{jchoose k}cdots{schoose t})每一个数拆分为(p)进制过后,都要满足上述关系,以第一位为例,就要满足(i_1geq j_1geq k_1geqcdotsgeq s_1geq t_1)。那么我们可以每一位单独考虑。最后每一位的结果乘起来就行。
因为矩阵大小为(p^n),就相当于(p)进制下(n)位的所有情况,每个数取值范围为([0,p-1]),这又是一个经典的组合数问题,我们用(x_i)表示值为(i)的数的个数有多少个,然后隔板算一算就行。
分析到这里,这个题已经明了了,最后那个求和式很简单,就是个等比数列求和。主要是将海绵宝宝矩阵的性质分析清楚,矩阵求和其实就是能够枚举组合数的所有情况。然后组合数拆分(p)进制单独考虑,最后再加个隔板。。。
写这个题的时候思路比较零碎,想到什么写的什么,大概就是这个意思。。
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/5/20 10:25:31
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e6 + 5, MOD = 1e9 + 7;

int qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1; 
    }
    return res;
}

int primes[N], tot;
bool vis[N];
int fac[N], inv[N];

void init() {
    for (int i = 2; i < N; i++) {
        if (!vis[i]) {
            vis[i] = true;
            primes[++tot] = i;
            if (tot >= 100000) break;
            for (ll j = 1ll * i * i; j < N; j += i) {
                vis[j] = true;
            }
        }
    }
    fac[0] = 1;
    for (int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qpow(fac[N - 1], MOD - 2);
    for (int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}

int C(int n, int m) {
    return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}

void run() {
    int n, c, k; cin >> c >> n >> k;
    int p = primes[c];
    int ans = 0;
    for (int i = 1; i <= k; i++) {
        int d = C(i + p, p - 1);
        if (d == 1) ans = (ans + n) % MOD;
        else ans = (ans + 1ll * d * (1 - qpow(d, n) + MOD) % MOD * qpow(1 - d + MOD, MOD - 2) % MOD) % MOD;
    }
    cout << ans << '
';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    init();
    int T; cin >> T; while(T--)
    run();
    return 0;
}

L - Pinball

模拟/物理题。

Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
#define D Point 
#define CD const D
#define EPS 1e-6
int dcmp(double x) {return fabs(x)<EPS?0:(x<0?-1:1);}
const double g=9.8;
struct Point { double x,y;};
	bool operator==(CD&l, CD&r) {return dcmp(l.x-r.x)==0 && dcmp(l.y-r.y)==0;}
	D operator+(CD&l, CD&r) {return (D){l.x+r.x,l.y+r.y};}
	D operator-(CD&l, CD&r) {return (D){l.x-r.x,l.y-r.y};}
	D operator*(CD&l, double a) {return (D){l.x*a, l.y*a};}
	D operator/(CD&l, double a) {return (D){l.x/a, l.y/a};}
	double len(CD&l) {return sqrt(l.x*l.x+l.y*l.y);}
	double dot(CD&l, CD&r) {return l.x*r.x+l.y*r.y;}
	D GetLineProj(CD&p, CD&a, CD&b) {
		D v=b-a;
		return a+v*(dot(v,p-a)/dot(v,v));
	}
	D mirror(CD&p, CD&a, CD&b) {
		D q=GetLineProj(p,a,b);
		return (D){2*q.x-p.x,2*q.y-p.y};
	}
double a,b,x,y;
	D bounce(CD&v) {
		D p1=(D){0,0};
		D p2=(D){-b,-a};
		D v2=mirror(v,p1,p2);
		v2.x=-(v2.x);
		v2.y=-(v2.y);
		return v2;
	}
	double run(CD&v) {
		double t=2*(b*v.x+a*v.y)/g/a;
		return t;
	}
#undef D
#undef CD
int n,m;
int main() {
	int t; scanf("%d", &t);
	while(0<t--) {
		scanf("%lf%lf%lf%lf",&a,&b,&x,&y);
		Point v;
		v.x=0; double down=y-(-x/a*b);
		v.y=-sqrt(2*g*down);
		Point pos; pos.x=x, pos.y=-x/a*b;
		int ans=0;
		while(pos.x<0) {
			ans++;
			v=bounce(v);
			double t=run(v);
			v.y-=g*t;
			pos.x+=v.x*t;
			pos.y=-pos.x/a*b;
		}
		printf("%d
", ans);
	}
}
原文地址:https://www.cnblogs.com/heyuhhh/p/12940445.html