test

这里是 lotus_f 在博客园的第一篇文章。


洛谷主页


测试 \(\LaTeX\)




引理:

引理 \(1\)

\(\gcd(x,y) = \gcd(y,x\bmod y)\)

显然。但是注意到如果 \(y \le k\) 的话尽管 \(x\) 可能为任何值但 \((x \bmod y) \le k\)

引理 \(2\)

对于任意正整数 \(x\),可将 \(x\) 分解成 \(a\times b \times c\),使得 \(a,b,c\) 这三个数都满足小于等于 \(\sqrt{x}\) 或者为质数。

证明:

\(a \le b \le c\),则大于 \(\sqrt{x}\) 的只可能是 \(c\)。当 \(c\) 大于 \(\sqrt{x}\) 且不为质数时,\(a \times b = \dfrac{x}{c} \le \sqrt{x}\)。设 \(c = d \times e, d \le e,d\not=1\),则有 \(d \le \sqrt{c} \le \sqrt{x}\)。可以将分解变为 \((a \times b) \times d \times e\)。因为每一次不满足的 \(c\) 都会变成 \(e\),而 \(d \not= 1\) 意味着 \(c\) 每次至少减半。\(c\) 这样操作许多次一定会 \(\sqrt{x}\)

引理 \(3\)

\(x\) 有一个因子 \(a\),则 \(\gcd(x,y)=\gcd(a,y) \times \gcd\left(\dfrac{x}{a},\dfrac{y}{\gcd(a,y)}\right)\)

证明(或者说是感性理解):\(a\) 已经“拿走”了 \(y\)\(\gcd(a,y)\) 了,再用 \(\dfrac{x}{a}\) 拿走的时候 \(y\) 只剩 \(\dfrac{y}{\gcd(a,y)}\) 了,所以 \(\dfrac{x}{a}\) 的贡献是 \(\gcd\left(\dfrac{x}{a},\dfrac{y}{\gcd(a,y)}\right)\)。乘起来即为答案。

做法

\(\mathcal O(1)\)\(\gcd(x,y)\)

假设之前已经知道了 \(x\) 可以分解为 \(a \times b \times c\)\(a,b,c\) 与引理 \(2\) 中的要求相同。

考虑使用引理 \(3\)\(x\) 有三个因子,\(a\) 能产生的贡献是 \(\gcd(a,y)\)\(b,c\) 能产生的贡献都在 \(a\) 没有“拿走”的 \(y\),所以 \(y \gets \dfrac{y}{\gcd(a,y)}\)。之后 \(b\) 能拿走的则为 \(\gcd(b,y)\),再将 \(y \gets \dfrac{y}{\gcd(b,y)}\)\(c\) 的贡献即为 \(\gcd(c,y)\)

但这个做法还是需要求 \(\gcd\) 啊?对于 \(a,b\),因为 \(a,b \le \sqrt{x}\),且根据引理 \(1\),可以预处理出 \([1,\sqrt{w}]\)\(w\) 为最大的值域,即最大的 \(x\))之间两数的 \(\gcd\),然后对于 \(\gcd(a \text{ or } b,y)\) 变成 \(\gcd(a \text{ or } b, y \bmod (a \text{ or } b))\) 就可以通过预处理得到答案了。

对于 \(c\),当 \(c \gt \sqrt{x}\) 时,即 \(c\) 一定为质数,因为一个数与质数的 \(\gcd\) 只会有两种可能:质数整除此数,答案为质数;质数不整除此数,答案为 \(1\)。当 \(c \gt \sqrt{x}\) 时,用与 \(a,b\) 一样的做法即可。

快速得到每一个数的拆分

设当前的数为 \(x\)

如果 \(x\) 为质数,则 \(x\) 可以分解为 \(1 \times 1 \times x\)

否则,设 \(x\) 最小的质因子为 \(p\),设 \(\dfrac{x}{p}\) 的拆分为 \(\dfrac{x}{p} = a_1 \times b_1 \times c_1,a_1 \le b_1 \le c_1\),则 \(x\) 的拆分为 \((a_1 \times p) \times b_1 \times c_1\)

下面给出证明:

首先,显然 \(b,c\) 是满足条件的。

现在考虑 \(a_1 \times p\)

  • \(p \le \sqrt[4]{x}\) 时,由于我们知道 \(a_1 \le b_1 \le c_1\),且 \(a_1 \times b_1 \times c_1=\dfrac{x}{p}\),所以 \(a_1 \le \sqrt[3]{\dfrac{x}{p}}\)。我们希望 \(a_1 \times p \le \sqrt{x}\),这时将 \(a_1\) 的最大值代入。 \(\sqrt[3]{\dfrac{x}{p}} \times p \le \sqrt{x}\)。同时变成六次方,变成 \(\dfrac{x^2}{p^2} \times p^6 \le x^3\),即 \(p^4 \le x\)。因为 \(p \le \sqrt[4]{x}\),所以显然成立。

  • \(p \gt \sqrt[4]{x}\) 时,再分两种情况:

  • \(a_1=1\),此时 \(a_1 \times p = p\),而 \(p \le \sqrt{x}\),所以满足条件。

  • \(a_1 \not= 1\),因为 \(\dfrac{x}{p}\) 拥有的因子,除了 \(1\) 一定大于等于 \(p\)(否则 \(p\) 就为那个因子了),所以有 \(p \le a_1\)。而又知道 \(a_1 \le b_1 \le c_1\),所以 \(p \le a_1 \le b_1 \le c_1\)。当 \(p \times a_1 \times b_1 \times c_1\) 取到最小值,应为 \(p^4 > x\),但又知道 \(p \times a_1 \times b_1 \times c_1=p \times \dfrac{x}{p}\),所以这种情况不会出现。

有了这个结论,便可以 \(O(n \log \log n)\) 埃筛求出每个数的分解了。(线性筛也可以,但我习惯写埃筛)




测试代码框:

2022 年联合省选 D1T1 考场代码经过调试后 AC 的代码:

// 输入,提取 #define 中的标识符与替换的内容,注意,替换的内容有可能有多个,所以放到一个 vector 里
// 提取非 #define 行中每一段“极长的字符串”到一个 vector 里,也就是可能被替换的(空格也需要替换,否则无法输出)
// 对于每一个 #define,标记开始的位置与结束的位置,每到一行,记录一个 vis,表示某个 #define 是否能用
// 对于每一行的“极长的字符串”,看是否能替换,如果能替换则替换,并插入 vector,否则不动
// 如果某一行替换过,则再次遍历替换,记录当前行已经用过的 #define,就不能再用了
// 输出,即对于 #define 输出 '\n',否则输出依次输出 vector 里的东西

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

int read()
{
    int now=0,f=1;
    char c=getchar();
    while(c<'0' || c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0' && c<='9')
    {
        now=now*10+c-'0';
        c=getchar();
    }
    return now*f;
}

string reads()
{
    string now="";
    char c=getchar();
    while(c=='\n' || c=='\r') c=getchar();
    while(c!='\n' && c!='\r' && c!=EOF)
    {
        now+=c;
        c=getchar();
    }
    return now;
}

int n;

struct line
{
    int th; // #define=1, #undef=2, null=0
    string bsf; // 标识符
    vector<string> s; // 两个 vector(共用一个),分别表示替换的东西以及一行的很多字符串
} a[110];

bool ifzm(char c)
{
    return (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_' || (c>='0' && c<='9');
}

vector<string> change(string s)
{
    vector<string> ans;
    ans.clear();
    int len=s.size();
    for(int i=0; i<len; ++i)
    {
        if(ifzm(s[i]))
        {
            int now=i;
            while(now<len && ifzm(s[now])) ++now;
            --now;
            ans.push_back(s.substr(i,now-i+1));
            i=now;
        }
        else
        {
            ans.push_back(s.substr(i,1));
        }
    }
    return ans;
}

string in;

map<string,int> wz; // 每一个标识符 #define 的行数(在哪里 #define 的)

bool nowuse[110]; // 当前能用的 #define

bool used[110]; // 递归定义中是否已经用过 #define 了

vector<string> change2(string s)
{
    //cout<<"change2: "<<s<<' '<<wz[s]<<' '<<nowuse[wz[s]]<<' '<<used[wz[s]]<<endl;
    vector<string> ans;
    ans.clear();
    if(!wz[s] || !nowuse[wz[s]] || used[wz[s]])
    {
        ans.push_back(s);
        return ans;
    }
    used[wz[s]]=1;
    for(int i=0; i<a[wz[s]].s.size(); ++i)
    {
        vector<string> now=change2(a[wz[s]].s[i]);
        for(int j=0; j<now.size(); ++j) ans.push_back(now[j]);
    }
    used[wz[s]]=0;
    //cout<<"len: "<<ans.size()<<' '<<s<<endl;
    //for(int i=0; i<ans.size(); ++i) cout<<ans[i]<<' ';
    //cout<<endl;
    return ans;
}

int main()
{
    n=read();
    for(int i=1; i<=n; ++i)
    {
        in=reads();
        int len=in.size();
        if(len>=2 && in[0]=='#' && in[1]=='d')
        {
            a[i].th=1;
            int now=8;
            while(now<len && ifzm(in[now])) ++now;
            --now;
            a[i].bsf=in.substr(8,now-8+1);
            a[i].s=change(in.substr(now+2,len-1-(now+2)+1));
            wz[a[i].bsf]=i;
        }
        else if(len>=2 && in[0]=='#' && in[1]=='u')
        {
            a[i].th=2;
            a[i].bsf=in.substr(7,len-1-7+1);
        }
        else
        {
            a[i].th=0;
            a[i].s=change(in);
        }
		if(a[i].th==1)
        {
            nowuse[i]=1;
        }
        else if(a[i].th==2)
        {
            nowuse[wz[a[i].bsf]]=0;
        }
        else
        {
            vector<string> ans;
            ans.clear();
            for(int j=0; j<a[i].s.size(); ++j)
            {
                vector<string> now=change2(a[i].s[j]);
                for(int k=0; k<now.size(); ++k)
                {
                    ans.push_back(now[k]);
                }
                /*
                do
                {
                    flag=0;
                    for(int k=l; k<=r; ++k)
                    {
                        if(wz[a[i].s[k]] && nowuse[wz[a[i].s[k]]])
                        {
                            flag=1;
                            a[i].s.erase(a[i].s.begin()+k);
                            int now=k;
                            for(int kk=0; kk<a[wz[a[i].s[k]]].s.size(); ++kk)
                            {
                                a[i].s.insert(a[i].s.begin()+now,a[wz[a[i].s[k]]].s[kk]);
                                ++now;
                            }
                            k=now-1;
                        }
                    }
                } while(flag);
                */
            }
            a[i].s=ans;
        }
		if(a[i].th) cout<<'\n';
        else
        {
            int len=a[i].s.size();
            for(int j=0; j<len; ++j) cout<<a[i].s[j];
            cout<<'\n';
        }
    }
    return 0;
}
posted @ 2022-05-03 17:52  lotus_f  阅读(61)  评论(1)    收藏  举报