牛客练习赛132个人题解(A-F)

牛客练习赛132

摘取了题目和有解释说明的题目样例

 

A.春

A-春_牛客练习赛132

原题

题目

在坐标轴 1 到 n 的 n 个位置上

有长度为 eq?a_i 的 n 根木棒

其下会围成一个不规则多边形(详见样例)

你可以多次交换不同相邻的木棒,求其下最大覆盖面积 S

输入描述

第一行输入 n

接下来输入一行包括 n  个正整数 eq?a_i  (eq?%241%5Cle%20n%20%5Cle%202%20%5Ctimes%2010%5E6%3B%201%5Cle%20a_i%20%5Cle%202%20%5Ctimes%2010%5E6%24)

输出描述

请输出 n 根木棒的最大围成面积 S

为了输出方便,请四舍五入后保留两位小数

示例

输入
3
3 1 2

输出
4.50

说明

5e182a66ddf9432bbf4ef825dda8d82d.png

  

交换第1、2根木棒,变成 [1, 3, 2]

面积从: (3+1)\*1/2+(1+2)\*1/2=3.5

变为:(1+3)\*1/2+(3+2)/2=4.5

不存在更大的面积

 

题解

思路1

为了使面积最大,我们应当让大的木棍尽可能的靠在一起。

对木棍进行排序,取出最大的两个木棍,设为l, r, 并重复以下操作

若果 l > r,此时将新的木棍放在左边的收益最大, 更新l为新的木棍

否则, 将新的木棍放在右边的收益最大, 更新r为新的木棍

每段的面积和即为所求值

时间复杂度o(nlogn)

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

bool cmp(ll x, ll y){
    return x>y;
}

void solve(){
    ll n = read();
    vector<ll> a(n);
    for(ll i=0;i<n;i++){
        a[i] = read();
    }
    if (n==1){
        printf("%.2f\n", 0);
        return ;
    }
    sort(a.begin(), a.end(), cmp);
    ll l=a[0], r=a[1];
    double ans=(l+r)*1.0/2;
    for(ll t=2;t<n;t++){
        if (l>r){
            ans += (l+a[t]) * 1.0/2;
            l = a[t];
        }
        else{
            ans += (r+a[t]) * 1.0/2;
            r = a[t];
        }
    }
    printf("%.2f\n", ans);
}


int main(){
    //ll t = read();
    ll t = 1;
    while (t--){
        solve();
    }
}

思路2(比赛的时候没想到)

考虑某种排序下木棍组成的图形的面积(eq?s_1%2Cs_2%2C...%2Cs_n

eq?S%20%3D%20%5Cfrac%7B%28s_1&plus;s_2%29%5Ctimes%201%7D%7B2%7D%20&plus;%20%5Cfrac%7B%28s_2&plus;s_3%29%5Ctimes%201%7D%7B2%7D&plus;...&plus;%5Cfrac%7B%28s_%7Bn-1%7D&plus;s_n%29%5Ctimes%201%7D%7B2%7D%20%3D%20%5Cfrac%7Bs_1&plus;s_n%7D%7B2%7D%20&plus;%20s_2&plus;s_3&plus;....&plus;s_%7Bn-1%7D

发现只有边缘的木棍只出现一次,贡献最小

只需将最小的两根木棍放在两边面积即为最大值

时间复杂度o(nlogn)

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

void solve(){
    ll n = read();
    vector<ll> a(n);
    for(ll i=0;i<n;i++){
        a[i] = read();
    }
    if (n==1){
        printf("%.2f\n", 0);
        return ;
    }
    sort(a.begin(), a.end());
    double ans = (a[0] + a[1])/2.0;
    for(ll i=2;i<n;i++){
       ans += a[i];
    }
    printf("%.2f\n", ans);
}


int main(){
    //ll t = read();
    ll t = 1;
    while (t--){
        solve();
    }
}

 

 

B.江

B-江_牛客练习赛132

原题

题目

在斗地主中连续的一组数组叫顺子,如eq?%24%5B3%2C4%2C5%2C6%2C7%5D%2C%20%5B9%2C8%2C7%2C6%2C5%5D%24;

在这里为了方便将 eq?%241%2C%202%24 也记入顺子中,且顺子长度没有任何限制,如 eq?%24%5B1%2C2%2C3%5D%2C%20%5B5%5D%24 均是顺子

可这样求最长顺子长度太无趣了...

于是聪明的你引入了鬼牌的概念、鬼牌可以变为 1 到 m 中任何你想要的牌

好了,现在求可以组成的最长顺子长度

 -赛后补充:

题目中最长顺子中的牌可以是原本中任意一张牌or鬼牌,组成的顺子必须连续无中断(排序后为公差为1的等差数列)

输入描述

第一行输入 n,m,k,分别表示有 n 张牌介于 1 到 m 之间,另外有 k 张鬼牌 (共n+k张牌)

第二行输入 n 个正整数 eq?a_i,表示 n 张牌的点数

eq?1%5Cle%20n%20%5Cle%205%5Ctimes%2010%5E5%3B%201%5Cle%20m%5Cle%202%5Ctimes10%5E9%3B%200%5Cle%20k%5Cle%202%5Ctimes10%5E9%3B1%5Cle%20a_i%20%5Cle%20m

输出描述

输出一个非负整数表示可行顺子的最大长度

 

题解

思路一

考虑到原来的牌中重复的对最长顺子无意义,因此先去重

手牌可以视为由一段段联系的牌组成, 即一系列 eq?%24b_%7Bl_i%7D%2Cb_%7Br_i%7D%20%24

使用滑动窗口遍历b数组, 不断使用鬼牌填补两段区间的空隙, 如果超出了k,则收缩左指针。

对一段使用了g张鬼牌填补的区间eq?%24l%2C%20r%24, 由其扩展的最大可能顺子长度为eq?min%28r%20-%20l%20&plus;%201%20&plus;%20k%20-%20g%2C%20n%29(不考虑合并其他区间的情况下)

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<long long,long long> PLL;
inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

inline void pt(ll x){if(x<0) putchar('-'),x=-x;if(x>9) pt(x/10);putchar(x%10+'0');}

void print(ll x){pt(x), puts("");}

int main() {
    ll n, m, k;
    n = read();
    m = read();
    k = read();
    vector<ll> a(n);
    for (ll i = 0; i < n; i++) {
        a[i] = read();
    }
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
    vector<PLL> b;
    ll l = 0;
    for(ll r=1;r<a.size();r++){
        if ((a[r]-a[r-1])!=1){
            b.push_back({a[l], a[r-1]});
            l = r;
        }
    }
    b.push_back({a[l], a[n-1]});
    if (b.size()==1){
        print(min(b[0].second - b[0].first + 1 + k, m));
    }
    else{
        
        ll current_width = b[0].second - b[0].first + 1;
        ll ans = min(current_width + k, m);
        ll l=0;
        ll g_num = 0;
        for(ll r=1;r<b.size();r++){
            g_num += b[r].first - b[r-1].second - 1;
            while(g_num > k&&l<=r){
                g_num -= b[l+1].first - b[l].second - 1;
                l++;
            }
            ans = max(ans, min(b[r].second - b[l].first + 1 + k - g_num, m));
        }
        print(ans);
    }
    
    
    return 0;
}

 

 

C.水 

C-水_牛客练习赛132

原题

题目

圆上有n等分点

你即将给其中个k点染上红色。

如果染色后,存在两个红点,其连线能平分圆的面积,则认为这种染色方式是美丽的;

请问,对k个点染色有多少种不同染色方式能画出一个美丽的图?

其中对n个点标记为eq?%241%2C2%2C3...n%24;如果两个圆A B染色不同,当且仅当存在1个点在A中被染为红色,而图B中没有染色

请将最终结果对 eq?10%5E9&plus;7%281000000007%20%29 取模

备注

圆上n等分点的定义是:在圆的周长上均匀地选择n个点,使得相邻两个点之间的弧长相等。

题解

两点连线能平分圆的面积, 则两点的连线过圆心,即关于圆心对称。

所以当n为奇数时,一定找不到满足条件的两个点。

考虑n为偶数的情况

当k<2时,结果为0

eq?k%3E%20%5Cfrac%7Bn%7D%7B2%7D时, 一定能找到两个点满足要求, 结果即为从n个位置中选k个的数量, 即eq?C_%7Bn%7D%5E%7Bk%7D

当 eq?k%5Cleqslant%20%5Cfrac%7Bn%7D%7B2%7D时,考虑不满足的个数,即两个点不在同一条过圆心的线上。先选k条线,有eq?C_%7B%5Cfrac%7Bn%7D%7B2%7D%7D%5E%7Bk%7D种,

再排列k个点,有eq?2%5Ek种。所以满足的个数为eq?C_%7Bn%7D%5E%7Bk%7D%20-%20C_%7B%5Cfrac%7Bn%7D%7B2%7D%7D%5E%7Bk%7D%20%5Ctimes%202%5Ek

为了压缩求eq?C_%7Bn%7D%5E%7Bk%7D的时间,预处理n!和其逆元

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<long long,long long> PLL;
inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
 
inline void pt(ll x){if(x<0) putchar('-'),x=-x;if(x>9) pt(x/10);putchar(x%10+'0');}
 
void print(ll x){pt(x), puts("");}

const ll mod = 1000000007;


ll fac[10000010];
ll ifac[10000010];
ll ksm(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b&1)ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
ll C(ll a,ll b){
    return fac[a] * ifac[b] % mod * ifac[a-b] % mod;
}



void solve(){
    ll n=read(),k=read();
    if (k<2||n%2!=0){
        print(0);
    }
    else if(k>n/2){
        print(C(n, k));
    }
    else{
        print((C(n, k) - ksm(2,k)*C(n/2, k)%mod + mod)%mod);
    }
}

int main() {
    fac[0] = ifac[0] = 1;
    for(int i = 1; i <= 1e7; i++){
        fac[i] = fac[i-1] * i % mod;
    }
    ifac[10000000] = ksm(fac[10000000], mod-2);
    for(int i = 1e7-1; i >= 1; i--){
        ifac[i] = ifac[i+1] * (i+1) % mod;
    }
    ll t = read();
    while(t--){
        solve();
    }
    return 0;
}

 

 

D.暖鸭

D-暖鸭_牛客练习赛132

原题

题目

还记得什么是阶乘吗,现在不考这个...

问给定  T 组数据,每组数据给定 l,r

在所有的eq?%24l%5Cle%20a%20%5Cle%20b%20%5Cle%20r%24 中,等可能地随机取一对(a, b)

求区间 [a, b] 至少包含一个平方数的概率

为了计算方便,最终答案乘上 (r-l+1)*(r-l+2) 后对 1000000007取模

输入

第一行输入一个 T,表示  T 组数据

接下来每行一组 l,r 如题所示eq?%241%5Cle%20T%20%5Cle%203%20%5Ctimes%2010%5E5%3B%201%20%5Cle%20l%20%5Cle%20r%20%5Cle%201%20%5Ctimes%2010%5E%7B16%7D%24

输出

输出一个非负整数表示最终答案

示例1

输入

3
1 1
1 2
1 3

输出

2
4
6

解释

[1,2] 中 a,b 有 [1,1], [1, 2], [2, 2]三种等可能区间,仅仅 [2, 2]没有包含完全平方数1

故答案为 2/3

输出 (2/3 * 3 * 2)%(1e9+7) = 4

 

题解

题目所求值为[a,b]内包含平方数的区间的两倍

不妨先求[a, b]内不包含平方数的区间数量

观察[1, 10]这个区间,枚举可得出不包含平方数的区间为区间[2, 3],  [5, 8], [10,10]的子区间

可以发现这些区间是以平方数分割的

 

eq?f%28l%2C%20r%29为[l, r]内不包含平方数区间的个数,eq?h%28x%29为长度为x的区间的子区间数量(令h(0)=0)

eq?f%28x%5E2%2C%28x&plus;1%29%5E2%29%20%3D%20%5Cfrac%7B%28%28x&plus;1%29%5E2-x%5E2-1%29*%28%28x&plus;1%29%5E2-1&plus;x%5E2&plus;1%29%29%7D%7B2%7D%20%3D%20%5Cfrac%7B2x%282x&plus;1%29%7D%7B2%7D%20%3D%20x%282x&plus;1%29

eq?f%281%2Cx%5E2%29%20%3D%20%5Csum_%7Bt%3D1%7D%5E%7Bx-1%7D%20t%5Ccdot%282t&plus;1%29%20%3D%20%5Cfrac%7B%28t-1%29%20%5Ccdot%20t%20%5Ccdot%282t-1%29%7D%7B3%7D%20&plus;%20%5Cfrac%7B%28t-1%29%5Ccdot%20t%7D%7B2%7D

eq?h%28x%29%20%3D%20%5Cfrac%7Bx%20%5Ccdot%20%28x&plus;1%29%7D%7B2%7D

 

令整数a, b 满足 eq?a%5Cleqslant%20l%5E2%20%2C%20r%5E2%5Cleqslant%20b(可用二分查找求)

则答案为eq?h%28b-a&plus;1%29%20-%20f%28a%2C%20b%29

eq?f%28a%2C%20b%29%20%3D%20f%28a%2C%20l%5E2-1%29&plus;f%28l%5E2%2C%20r%5E2%29&plus;f%28r%5E2&plus;1%2Cb%29,有以下结果

1. eq?%5Ba%2C%20l%5E2-1%5D以及 eq?%5Br%5E2&plus;1%2C%20b%5D内必定没有平方数,所以eq?f%28a%2Cl%5E2-1%29&plus;f%28r%5E2&plus;1%2C%20b%29%20%3D%20h%28l%5E2-a%29%20&plus;%20h%28b-r%5E2%29

(当a=1, b=1时, l=1, r=1, 此时 eq?l%5E2-1%20%3D%200%20%3C%20l%2C%20r%5E2&plus;1%20%3D%202%20%3Er, 但右式答案仍正确,实际使用时直接计算即可)

2 .eq?f%28l%5E2%2C%20r%5E2%29%20%3D%20f%281%2C%20r%5E2%29%20-%20f%281%2C%20l%5E2%29

 

最终式子为

eq?h%28b-a&plus;1%29%20-%20%28h%28l%5E2-a%29%20&plus;%20h%28b-r%5E2%29%20&plus;%20f%281%2C%20r%5E2%29%20-%20f%281%2C%20l%5E2%29%29

eq?%3D%20%28%5Cfrac%7B%28b-a&plus;1%29%28b-a&plus;2%29%7D%7B2%7D%29%20-%20%28%28%5Cfrac%7B%28r-1%29%20%5Ccdot%20r%20%5Ccdot%282r-1%29%7D%7B6%7D%20&plus;%20%5Cfrac%7B%28r-1%29%5Ccdot%20r%7D%7B4%7D%29%20-%20%28%5Cfrac%7B%28l-1%29%20%5Ccdot%20l%20%5Ccdot%282l-1%29%7D%7B6%7D%20&plus;%20%5Cfrac%7B%28l-1%29%5Ccdot%20l%7D%7B4%7D%29%29%20&plus;%20%5Cfrac%7B%28l%5E2-a&plus;1%29%28l%5E2-a&plus;2%29%7D%7B2%7D&plus;%20%5Cfrac%7B%28b-r%5E2&plus;1%29%28b-r%5E2&plus;2%29%7D%7B2%7D%29

  

如果发现自己样例测试全部通过提交后仍为wa,建议带入eq?10%5E%7B16%7D测试一下每个部分会不会溢出(最保险的就是求一步取一次模)

 

代码如下

#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const ll MOD = 1000000007;
 
inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
  
inline void pt(ll x){if(x<0) putchar('-'),x=-x;if(x>9) pt(x/10);putchar(x%10+'0');}
  
void print(ll x){pt(x), puts("");}

ll ksm(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b&1)ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans%MOD;
}

ll sqrt(ll a)
{
    ll l=0,r=1e9;
    while(l<r){
        ll mid=(l+r)>>1;
        if(mid*mid<=a){
            l=mid;
        }
        else{
            r=mid-1;
        }
        if(l==r-1){
            if(r*r<=a){
                l=r;
            }
            break;
        }
    }
    return l;
}

ll g(ll x) {
    //f(1, x*x)
    x %= MOD;
    ll l = (x - 1 + MOD) % MOD;
    l = (l * x) % MOD;
    l = (l * (2 * x + MOD - 1) % MOD) % MOD;
    l = l * ksm(3,MOD-2) %MOD; 
    ll r = (x - 1 + MOD) % MOD;
    r = (r * x) % MOD;
    r = r * ksm(2,MOD-2) % MOD;
    
    return (l + r) % MOD;
}

ll h(ll x){
    //长度x的区间的子区间数量
    ll l = x%MOD;
    l = l * ((x+1)%MOD) %MOD;
    l = l * ksm(2, MOD-2)%MOD;
    return l;
}

void solve(){
    ll a = read(), b = read();
    ll l = sqrt(a-1)+1, r = sqrt(b);
    if (r<l){
        print(0);
    }
    else{
        print((((h(b-a+1)- h(l*l-a) - h(b-r*r) - (g(r)-g(l)))%MOD+MOD)*2+MOD)%MOD);  
    }  
}

int main(){
    ll T = read();
    while(T--){
        solve();
    }
}

 

 

E.先知

原题

题目

在n*m大小的二维矩阵中每个格子的初始高度为 eq?%24a_%7Bij%7D%24

俗话说万丈高楼平地起...

你决定使用魔法:对于每个格子 eq?%24a_%7Bij%7D%24 如果其四周(上下左右)均有格子,且四周格子高度均大于等于 eq?%24a_%7Bij%7D%24 时实际高度会 +1,即 eq?a_%7Bij%7D%20%3D%20a_%7Bij%7D%20&plus;1

你将会夜以继日充不停息,从上到下,从左到右遍历整个矩阵对于每个格子均使用一次魔法

但是,慢慢的,你好像发现时间足够长久后,貌似整个矩阵高度均不会发生变化

求最终矩阵的形态;为了输出方便,请输出全部 eq?%24a_%7Bij%7D%24 的和以及 eq?%24a_%7Bij%7D%24 的异或和

输入

第一行输入 n,m 两个整数表示 n行m列的矩阵

接下来 n 行,每行 m 个 eq?%24a_%7Bij%7D%24 表示初始平地的高度

eq?1%5Cle%20n%2Cm%20%5Cle%202%20%5Ctimes%2010%5E3%3B%201%5Cle%20a_%7Bij%7D%20%5Cle%201%20%5Ctimes%2010%5E9

输出

输出一行两个整数

分别表示最终状态下 eq?%24a_%7Bij%7D%24 的和 以及 eq?%24a_%7Bij%7D%24 的异或和

示例1
输入

4 4

2 4 2 2

3 2 2 2

2 2 2 2

2 2 2 2

输出

40 0

说明

第一次遍历:

遍历到第 2 行第 2 列的数字2

有2<=min(4,3,2,2),

故变为

2 4 2 2

3 3 2 2

2 2 2 2

2 2 2 2

遍历到第 2 行第 3 列的数字2,

变为

2 4 2 2

3 3 3 2

2 2 2 2

2 2 2 2

遍历到第 3 行第 2 列的数字2,变为

2 4 2 2

3 3 3 2

2 3 2 2

2 2 2 2

遍历到第 3 行第 3 列的数字2,变为

2 4 2 2

3 3 3 2

2 3 3 2

2 2 2 2

第二次遍历:

遍历到第 2 行第 2 列的数字3 有3<=min(4,3,3,3),

故变为

2 4 2 2

3 4 3 2

2 3 3 2

2 2 2 2

此后不会有任何改变

和为 2+4+2+2+3+4+3+2+2+3+3+2+2+2+2+2=40

异或和为2^4^2^2^3^4^3^2^2^3^3^2^2^2^2^2=0 

题解

不难发现, 一个点的值最大能到的值取决于周围最小值

只需维护所有点中的最小值及其位置(优先队列), 每次取出最小值更新周围即可

全部更新完后遍历求答案

#include <bits/stdc++.h>
using namespace std;
  
typedef long long ll;
typedef pair<long long,long long> PLL;
typedef tuple<ll,ll,ll> TLLL;
  
inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
   
ll maze[2009][2009];
ll vis[2009][2009];
 
 
struct cmp {
    bool operator()(TLLL x, TLLL y) {
        return get<2>(x) > get<2>(y); 
    }
};
 
int main(){
    ll n = read(), m = read();
    for(ll i=0;i<n;i++){
        for(ll j=0;j<m;j++){
            maze[i][j] = read();
        }
    }
    priority_queue<TLLL, vector<TLLL>, cmp> q;
    for(ll x=0;x<n;x++){
        for(ll y=0;y<m;y++){
            if ((x==0||x==n-1)||(y==0||y==m-1)){
                q.push(TLLL{x, y, maze[x][y]});
                vis[x][y] = 1;
            }
        }
    }
     
    vector<PLL> move = {{0, 1}, {0, -1}, {1, 0}, {-1 ,0}};
    while(!q.empty()){
        TLLL t = q.top();
        q.pop();
        for(PLL mo:move){
            ll x = get<0>(t) + mo.first;
            ll y = get<1>(t) + mo.second;
            if(1<=x&&x<=n-2&&1<=y&&y<=m-2&&!vis[x][y]){
                maze[x][y] = max(maze[x][y], get<2>(t) + 1);
                q.push(TLLL{x, y, maze[x][y]});
                vis[x][y] = 1;
            }
        }
         
    }
    ll ans1=0;
    ll ans2=0;
    for(ll i=0;i<n;i++){
        for(ll j=0;j<m;j++){
            ans1 += maze[i][j];
            ans2 ^= maze[i][j];
        }
    }
    cout<< ans1<<' '<<ans2<<'\n';
}

 

 

F.。

F-。_牛客练习赛132

原题

题目

多组样例 T

每输入三个数 a,b,c 表示木棒长度

你可以对任意边多次花费 1 的代价将某条边长度 +1 或 −1

使得最终的 a,b,c 可以组成直角三角形

求最小代价

输入

第一行输入 T 表示 T 组样例

每组样例输入三个正整数 a,b,c

eq?%241%5Cle%20T%20%5Cle%2020%3B%201%5Cle%20a%2Cb%2Cc%20%5Cle%202%20%5Ctimes%2010%5E%7B5%7D%24

输出

每组样例一个整数表示答案

如果无法构成直角三角形输出 −1

 

题解

a,b,c的范围很小, 可以预处理所有的组成直角三角形的三元组(即毕达哥拉斯三元组

代码如下

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef tuple<ll,ll,ll> TLLL;
const ll inf =  0x3f3f3f3f;

inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
 
inline void pt(ll x){if(x<0) putchar('-'),x=-x;if(x>9) pt(x/10);putchar(x%10+'0');}
 
void print(ll x){pt(x), puts("");}
 

vector<TLLL> findAll() {
    vector<TLLL> all;
    ll limit = 500000;
    for (ll m = 2; m * m <= limit; ++m) {
        for (ll n = 1; n < m; ++n) {
            if ((m - n) % 2 == 1 && gcd(m, n) == 1) {//生成本原毕达哥拉斯三元组
                ll a = m * m - n * n;
                ll b = 2 * m * n;
                ll c = m * m + n * n;
 
                ll mn = min({a, b, c}), mx = max({a, b, c});
                b = a + b + c - mn - mx;
                a = mn;
                c = mx;
                if (c > limit) break;
                for (ll k = 1; k * c <= limit; ++k) {
                    all.emplace_back(k * a, k * b, k * c);
                }
            }
        }
    }

    return all;
}


vector<TLLL> all;

void solve(){
    ll a = read(),b=read(),c=read();
    ll ans = inf;
    ll mn = min({a, b, c}), mx = max({a, b, c});
    b = a + b + c - mn - mx;
    a = mn;
    c = mx;
    for (const auto &triple : all) {
        ll na = get<0>(triple);
        ll nb = get<1>(triple);
        ll nc = get<2>(triple);
        ans = min(ans, abs(na - a) + abs(nb - b) + abs(nc - c));
     }
    print(ans);
}


int main() {
    all = findAll();
    ll T=read();
    while(T--){
        solve();
    }
    return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值