CF1916D Mathematical Problem

· · 题解

思路

很不错的人类智慧题。

拿到以后,完全没有思路,看到数据范围,感觉是什么 n^2\log n 的逆天做法,但是又完全没思路,看后面的题感觉没希望,就在这道题死磕。

先打了个暴力程序,发现平方数太多,没什么规律,就拿了个 map 统计一下那些出现数字方案拥有的平方数比较多

程序如下:

#include<bits/stdc++.h>
using namespace std;
multiset<int>s;
map<multiset<int>,int>mp;
int main()
{
    for(int i=1;i<=200000;++i)
    {
        long long k=1ll*i*i;
        if(k<100000000) continue;//此程序是九位数的情况,想试其他情况的就改这里的值即可
        if(k>999999999) break;
        while(k) s.insert(k%10),k/=10;
        ++mp[s],s.clear();
    }
    for(auto i:mp)
    {
        if(i.second>=9)//这里也要改
        {
            for(auto j:i.first)
            {
                cout<<j<<" ";
            }
            puts("");
        }
    }
    return 0;
}

先试一试五位数的情况,发现下面的数字组合都可以存在五个以上的平方数,除了样例给的还有一组。

再试一试七位数的情况,发现符合条件的数字组合更多了。

一看,发现了一个很重要的规律,都有 0~ \cdots ~0 ~1 ~6 ~9

所以猜测所有情况都可以由若干个 01~6~9 构成。

再试一试九位数,发现也是如此。

于是,改了改暴力程序,让程序输出符合条件的平方数,如下:

#include<bits/stdc++.h>
using namespace std;
multiset<int>s;
map<multiset<int>,int>mp;
int main()
{
    multiset<int>tmp;
    tmp.insert(0),tmp.insert(0),tmp.insert(0),tmp.insert(0),tmp.insert(0),tmp.insert(0),tmp.insert(1),tmp.insert(6),tmp.insert(9);//同样是九位数的程序,其他情况注意更改参数
    for(int i=1;i<=200000;++i)
    {
        long long k=1ll*i*i;
        if(k<100000000) continue;
        if(k>999999999) break;
        while(k) s.insert(k%10),k/=10;
        if(s==tmp) printf("%lld\n",1ll*i*i);
        s.clear();
    }
    return 0;
}

先试一试五位数的,发现程序给的是:

10609
16900
19600
61009
90601
96100

研究了一下,发现首先又 1690\cdots1960\cdots9610\cdots,这三个很显然,都是 169196961 乘以了 10^k,其中 k 为偶数,所以一定是平方数。

剩下的就是 10\cdots60\cdots90\cdots,也就是 16916 之间和 69 之间插入相同数量的零,再在后面放若干个零构成的,首先因为插入的零是偶数,并且总数字个数是奇数,再加上有 169 三个数字,所以后面的零一定是偶数个,所以后面加的一定正确,只需要验证 10\cdots60\cdots9 是否一定正确即可,发现就是 10\cdots3 的平方,所以一定正确。

那么同理 96116 之间和 69 之间插入相同数量的零,再在后面放若干个零构成的数也一定是平方数,也是正确的。

使用暴力程序在七位数和九位数验证,发现是正确的。

再计算一下我们发现了多少个平方数了,在其中插入的零可以是 [0,\frac{n-1}2-1] 个,一共有 \frac{n-1}2 个,169961都可以,一共有 n-1 个,再加上 1960\cdots 的一个,刚好 n 个。

其实发现还有形如 61009 之类的数也满足条件,但是我们已经不需要了。

根据上面发现的构造方案可以很轻松的写出程序。

AC code

#include<bits/stdc++.h>
using namespace std;
long long T,n,a,sum,num;
int main()
{
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);
        if(n==1){puts("1");continue;}//注意特判n=1
        printf("169");
        for(int i=4;i<=n;++i) printf("0");
        puts("");
        printf("961");
        for(int i=4;i<=n;++i) printf("0");
        puts("");
        printf("196");
        for(int i=4;i<=n;++i) printf("0");
        puts("");
        for(int i=1;i<n/2;++i)
        {
            printf("1");
            for(int j=1;j<=i;++j) printf("0");
            printf("6");
            for(int j=1;j<=i;++j) printf("0");
            printf("9");
            for(int j=3+2*i;j<n;++j) printf("0");
            puts("");
            printf("9");
            for(int j=1;j<=i;++j) printf("0");
            printf("6");
            for(int j=1;j<=i;++j) printf("0");
            printf("1");
            for(int j=3+2*i;j<n;++j) printf("0");
            puts("");
        }
    }
    return 0;
}