Codeforces Round #844 (Div. 1 + Div. 2, based on VK Cup 2022 - Elimination Round) D

该文介绍了一种方法来解决与完全平方数相关的问题。通过枚举所有数对并寻找它们的双因子对,当两数之差为偶数时,可以表示为(a+b)(a-b)的形式。使用动态规划dp[i][x]来表示在枚举到第i个数时,加上x能得到的完全平方数的个数。文章强调了n≤50的条件使得双重循环枚举可行,并提供了相应的C++代码实现。
摘要由CSDN通过智能技术生成

1781D - Many Perfect Squares

分析

对于每组,若均为完全平方数,则存在:

所以枚举所有,对于每个,枚举其所有“双因子对”,若两个因子之差为偶数,则一定能写成(a+b)(a-b)的形式。两个因子较大的为big,较小的为small,则a=(big+small)/2; b=(big-small)/2(解方程组)。对于此组a、b,

以f[i][x]表示第一层循环枚举到a[i]时,加上x可以具有的完全平方的个数。

若当前x是第一次被加入,则需要把当前第一指针和第二指针指向的两个数都统计到答案个数里:

否则,只需要把第二指针指向的数统计到答案个数里:

反思

两个数加同一个数再相减相当于原数相减,而n<=50的数据暗示了双重循环枚举所有数对的可行性。

没想到dp的状态表示——同一个x 下,一系列a[i]的完全平方数是唯一的。

代码实现

#include<bits/stdc++.h>
#define ll long long
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
int n;
ll a[53],ans,dif;
map<ll, int>f[53]; 
void solve(){
    ans=1;
    cin>>n;
    rep(i,1,n){
        cin>>a[i];
        f[i].clear();
    }
    rep(i,1,n-1){
        rep(j,i+1,n){
            dif=a[j]-a[i];
            rep(k,1,sqrt(dif)){
                if(dif%k==0 && (dif/k-k)%2==0){
                    ll big=(dif/k+k)>>1;
                    ll small=(dif/k-k)>>1;
                    if(small*small<a[i]) continue;
                    ll x=small*small-a[i];
                    //a[i]+x=small*small, big同理  
                    if(f[i][x]!=0) f[j][x]=max(f[j][x], f[i][x]+1);
                    else f[j][x]=2; 
                    if(f[j][x]>ans){
                        ans=f[j][x];
                    } 
                    /* cup-pyy的写法,与上几行写法等价(每个big/small对应着一个x) 
                    //如果small曾经被作为big纳入,则只需要加上现在的big 
                    if(f[i][small]!=0) f[j][big]=max(f[j][big], f[i][small]+1);
                    //否则得把当前small和big都纳入 
                    else f[j][big]=2; 
                    if(f[j][big]>ans){
                        ans=f[j][big];
                    } 
                    */
                }
            }
        }
    }
    cout<<ans<<"\n";
} 
int main(){
    ios_base::sync_with_stdio(false); 
    cin.tie(0); 
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值