• SDNU_ACM_ICPC_2021_Winter_Practice_2nd [个人赛]


    传送门

    A - Different Divisors

    题意:

    输入一个数x,让你找到一个至少有四个因子且这四个因子的大小差至少为x的一个最小的数

    思路:

    首先我们假设要求的这个数是ans,要让她最小,则其中四个因子的两个就是1和ans本身,这样我们只需要找到剩下的两个因子a,b;使得1,a,b三个数直接的大小差至少为x即可。但是,如果你找的a和b是一个合数,那这个合数至少有一个小于本身且不同于1和它本身的一个因子,这个因子也一定是ans的因子,这样的话就不能让1和这个因子的大小差不小于x了,所以说我们必须要找素数。

    第一步先打个素数表,然后再进行一次循环,找两个满足条件的数就break掉

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <stdlib.h>
    #include <sstream>
    #include <map>
    #include <set>
    using namespace std;
    typedef long long ll;
    ll tr[100005];
    inline int IntRead()
    {
        char ch = getchar();
        int s = 0, w = 1;
        while(ch < '0' || ch > '9')
        {
            if(ch == '-')
                w = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            s = s * 10 + ch - '0',
            ch = getchar();
        }
        return s * w;
    }
    bool is_prime(ll n)///素数判定,用来打表的
    {
        if(n == 1)  return 1;
        for(int i = 2; 1ll * i * i <= n; ++i)
        {
            if(n % i == 0)  return 0;
        }
        return 1;
    }
    int main()
    {
        ll len = 0;
        for(int i = 1; i <= 100005; i++)//打表
        {
            if(is_prime(i) == 1)
                tr[len++] = i;
        }
        ll n, t;
        //cout<<tr[0];
        t = IntRead();
        while(t--)
        {
            n = IntRead();
            ll a = 1, ans = 1, sum = 0;//a是指上一个的因子,ans是我们最终的结果,sum用来计数
            for(int i = 1; i < 100005; i++)
            {
                if(tr[i] - a >= n)
                {
                    ans *= tr[i];//如果找到了就乘起来
                    a = tr[i];//更新a的值
                    sum++;//用来计数
                }
                if(sum == 2)//满两个数就退出循环
                {
                    cout<<ans<<endl;
                    break;
                }
            }
        }
        return 0;
    }
    

    D - 这不是签到题

    题意:

    原来你有两个二进制数a和b,而d是这两个二进制数按位相加,不会进位的加法,每个位最大是2,数字大的就是大,比如102 > 21 021 = 21,还有的是这个d数字不喜欢重复的东西,所以会把自身连续的重复的数字变成不重复的,比如:1211 ---> 121 , 022000 ---> 020。不幸的是,Mike把a弄丢了,为了让他开心,你必须找到一个a,使得d的值最大

    思路:

    因为是找最大的数,我们肯定是让每一位都尽可能的大,但是还得考虑不能出现重复的,因为一旦出现重复的数,你的位数就会少,肯定就会小,举个栗子:

    {{uploading-image-234102.png(uploading...)}}

    b是001011,要让d最大,那么a的第一位绝对是1,这个时候d的第一位是1;要防止d串去重,我们只能让a的第二位是0,所以d的第二位是0;下一个就可以不管b是几,直接让a取最大,也就是a的第三位是1,这个时候d的第三位是1+1也就是2;因为第三位是2,我们要避免第四位也是2而被去重了,所以就要看b串的第四位是0,我们就让a的第四位是1,所以d的第四位是1……就这样一直判断下去

    脱离这个例子,我们会发现d的第i位是由d的第i-1位和b的第i位这两个数来决定的,所以可以分情况讨论一下:

    当d的第i-1位是2的时候,如果bi是0,则ai取1,如果bi是1,则ai取0。

    当d的i-1位是1的时候,如果bi是0,则ai取0,;如果bi是1,则ai取1

    当d的第i-1位是0的时候,就可以不用管bi的值,直接让ai取1。

    记得每次都要更新ai,bi,di的值

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <stdlib.h>
    #include <sstream>
    #include <map>
    #include <set>
    using namespace std;
    typedef long long ll;
    inline int IntRead()
    {
        char ch = getchar();
        int s = 0, w = 1;
        while(ch < '0' || ch > '9')
        {
            if(ch == '-')
                w = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            s = s * 10 + ch - '0',
            ch = getchar();
        }
        return s * w;
    }
    int main()
    {
        int t, n, a, b,c;
        string s;
        t = IntRead();
        while(t--)
        {
            n = IntRead();
            cin>>s;
            c = 1;
            b = s[0] - '0';
            a = (s[0] - '0') + c;
            printf("%d",c);
            //cout<<a<<' '<<b<<' '<<c<<endl;
            for(int i = 1; i < s.size(); i++)
            {
                b = s[i] - '0';
                if(a == 1)
                {
                    if(b == 0)
                    {
                        c = 0;
                        printf("0");
    
                    }
                    else
                    {
                        c = 1;
                        printf("1");
                        //cout<<1;
                    }
    
                }
                else if(a == 2)
                {
                    if(b == 1)
                    {
                        c = 0;
                        printf("0");
                        ///cout<<c;
                    }
                    else
                    {
                        c = 1;
                        printf("1");
                        //cout<<c;
                    }
                }
                else if(a == 0)
                {
                    c = 1;
                    printf("1");
                    //cout<<c;
                }
                a = b + c;
                //cout<<a<<' '<<b<<' '<<c<<endl;
            }
            cout<<endl;
        }
        return 0;
    }
    

    G - A Simple Problem with Integers

    线段树区间修改加区间查询的板子题

    本来抱着wa的心态把之前wa了的板子打上去了,只不过这次把所有的int全换成了ll,结果AC了,可给我高兴坏了.jpg

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <stdlib.h>
    #include <sstream>
    #include <map>
    #include <set>
    using namespace std;
    //#define endl '/n'
    //#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
    typedef long long ll;
    inline int IntRead()
    {
        char ch = getchar();
        int s = 0, w = 1;
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') w = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            s = s * 10 + ch - '0',
            ch = getchar();
        }
        return s * w;
    }
    ll tr[100005], d[400005], b[400050];
    ll n, a, f, c, k, q;
    void built(ll s, ll t, ll p)
    {
        if(s == t)
        {
            d[p] = tr[s];
            return;
        }
        ll m = (s + t) / 2;
        built(s, m, p * 2);
        built(m + 1, t, p * 2 + 1);
        d[p] = d[p * 2] + d[p * 2 + 1];
    }
    void update2(ll l, ll r, ll c, ll s, ll t, ll p)
    {
        if(l <= s && t <= r)
        {
            d[p] += c * (t - s + 1);
            b[p] += c;
            return;
        }
        ll m = (s + t) / 2;
        if(b[p])
        {
            d[p * 2] += b[p] * (m - s + 1);
            d[p * 2 + 1] += b[p] * (t - m);
            b[p * 2] += b[p];
            b[p * 2 + 1] += b[p];
            b[p] = 0;
        }
        if(l <= m)
            update2(l, r, c, s, m, p * 2);
        if(r > m)
            update2(l, r, c, m + 1, t, p * 2 + 1);
        d[p] = d[p * 2] + d[p * 2 + 1];
    }
    
    
    ll getsum2(ll l, ll r, ll s, ll t, ll p)
    {
        if(l <= s && t <= r)
        {
            return d[p];
        }
        ll m = (s + t) / 2;
        if(b[p])
        {
            d[p * 2] += b[p] * (m - s + 1);
            d[p * 2 + 1] += b[p] * (t - m);
            b[p * 2] += b[p];
            b[p * 2 + 1] += b[p];
            b[p] = 0;
        }
        ll sum = 0;
        if(l <= m)
            sum += getsum2(l, r, s, m, p * 2);
        if(r > m)
            sum += getsum2(l, r, m + 1, t, p * 2 + 1);
        return sum;
    }
    
    int main()
    {
        char a;
        n = IntRead();
        q = IntRead();
        for(int i = 1; i <= n; i++)
            tr[i] = IntRead();
        built(1, n, 1);
        while(q--)
        {
            cin>>a;
            if(a == 'Q')
            {
                f = IntRead();
                c = IntRead();
                cout<<getsum2(f, c, 1, n, 1)<<'
    ';
            }
            else
            {
                f = IntRead(), c = IntRead(), k = IntRead();
                update2(f, c, k, 1, n, 1);
    //            for(int i = 1; i <=  10; i++)
    //                cout<<d[i]<<' ';
            }
        }
        return 0;
    }
    

    H - 最少拦截系统

    题意:

    导弹拦截系统发射的炮弹第一次可以到达任意高度,该系统之后所能达到的高度不会高于上次拦截的高度,问你需要几套系统能完全拦截导弹

    思路:

    当时我一看这个题,就寻思这不是那动态规划嘛,之前比较懒没学,就直接跳过了,做到后面发现这是动态规划吗,怎么做出来十几个人,就感觉不对劲,就不抱希望地回头仔细读题,却发现可以直接贪心做

    我是先取了一个数组d,用来存需要的拦截系统,最坏的情况下是一个炮弹比一个高,所以数组得开三万,初始只有一个系统,其值为第一个炮弹的高度,两个for循环,外循环是对炮弹的高度 tr[i] ,内循环是对出现的系统的高度d[j],如果tr[i] > 所有的d[j],说明前面的所有系统都不好使了,需要一个新系统,这个新系统的d[len++] = tr[i];如果tr[i] < 某一个的d[j],说明第j个系统能拦住他,就可以更新一下这个d[j] 的值为tr[i]。最后只需要输出数组的长度即可

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <stdlib.h>
    #include <sstream>
    #include <map>
    #include <set>
    using namespace std;
    inline int IntRead()
    {
        char ch = getchar();
        int s = 0, w = 1;
        while(ch < '0' || ch > '9')
        {
            if(ch == '-')
                w = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            s = s * 10 + ch - '0',
            ch = getchar();
        }
        return s * w;
    }
    int tr[30005], d[30005];
    int main()
    {
        int n;
        while(cin>>n)
        {
            for(int i = 1; i <= n; i++)
                tr[i] = IntRead();
            d[1] = tr[1];
            int len = 1, k = 0;//数组长度为len,初始为1,k用来判断是否需要新系统
            for(int i = 2; i <= n; i++)
            {
                k = 0;//每次循环都要初始化k的值
                for(int j = 1; j <= len; j++)
                {
                    if(tr[i] <= d[j])
                    {
                        d[j] = tr[i];
                        k = 1;
                        break;//找到了就跳出,进行下一个炮弹的拦截
                    }
                }
                if(k == 0)//k == 0说明没找到能拦截的系统,就得添加新系统
                {
                    d[++len] = tr[i];
                }
            }
            cout<<len<<endl;
        }
        return 0;
    }
    
    

    I - How Many Tables

    并查集板子题,但是记得要初始化数组,一不小心就wa了.jpg

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <stdlib.h>
    #include <sstream>
    #include <map>
    #include <set>
    using namespace std;
    inline int IntRead()
    {
        char ch = getchar();
        int s = 0, w = 1;
        while(ch < '0' || ch > '9')
        {
            if(ch == '-')
                w = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            s = s * 10 + ch - '0',
            ch = getchar();
        }
        return s * w;
    }
    int tr[10005];
    int find1(int x)
    {
        if(tr[x] == x)
            return x;
        if(tr[x] != x)
        {
            tr[x] = find1(tr[x]);
            return tr[x];
        }
    }
    void ran(int a, int b)
    {
        int x = find1(a);
        int y = find1(b);
        if(x != y)
            tr[y] = x;
    }
    int main()
    {
        int n, m, a, b, t;
        t = IntRead();
        while(t--)
        {
            int sum = 0;
            memset(tr, 0, sizeof(tr));
            n = IntRead();
            m = IntRead();
            for(int i = 1; i <= n ; i++)
            {
                tr[i] = i;
            }
            for(int i = 1; i <= m; i++)
            {
                a = IntRead();
                b = IntRead();
                ran(a, b);
            }
            for(int i = 1; i <= n; i++)
            {
                if(tr[i] == i)
                    sum++;
            }
            printf("%d
    ",sum);
            //cout<<sum<<endl;
        }
        return 0;
    
    
    }
    

    M - Intersecting Lines

    题意:

    给你四个点,(x1,y1)(x2,y2)(x3,y3)(x4,y4),前两个点构成一条直线,后两个点构成一条直线,问你这两条直线 位置关系

    思路:

    看起来挺简单的,其实你得对解析几何了解的比较透彻才行

    我用的是最老土的办法:设直线,推公式,而非那些花里胡哨的向量计算法(好吧,其实是我忘了向量那些公式是什么了……手动狗头

    相信学过高中数学的同志们都知道考试最后一个解析几何的题,基本上都要考虑斜率不存在的特殊情况!

    对,这个题你得先考虑斜率不存在的时候,两个直线的斜率都不存在的时候,你还得判断这两条直线是不是同一条直线(我就是wa在了这里!!!当时想了半天都觉得自己思路没问题,后来就下定决心如果再找不出错就去吃饭,结果一不小心找到了.jpg),判断完斜率不存在之后就可以直接算k1,b1,k2,b2,然后推个公式带进去即可。还要注意题目对输出的一些要求就能AC。

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <stdlib.h>
    #include <sstream>
    #include <map>
    #include <set>
    using namespace std;
    int main()
    {
    	int n;
        double  x1, x2, x3, x4, y1, y2, y3, y4;
        scanf("%d",&n);
        printf("INTERSECTING LINES OUTPUT
    ");
        while(n--)
        {
          scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
    
            if(x1 == x2 && x3 == x4)//同时没有斜率
            {
            	if(x1 == x3)//判断是不是重合了
            		cout<<"LINE"<<endl;
            	else
            		cout<<"NONE"<<endl;
            }
            else if(x1 == x2 && x3 != x4)//一个有斜率一个没有斜率
            {
                double k2,b2,x,y;
                k2 = (y4 - y3) / (x4 - x3);//公式,手推
                b2 = (x4 * y3 - x3 * y4) / (x4 - x3);
                x = x1;
                y = k2 * x + b2;
                printf("POINT ");
                printf("%.2lf %.2lf
    ", x, y);
    //            cout<<x<<' '<<y<<endl;
            }
            else if(x1 != x2 && x3 == x4)//同样是一个有斜率一个没有
            {
                double k1,b1,x,y;
                k1 = (y2 - y1) / (x2 - x1);
                b1 = (x2 * y1 - x1 * y2) / (x2 - x1);
                x = x3;
                y = k1 * x + b1;
                printf("POINT ");
                printf("%.2lf %.2lf
    ", x, y);
                //cout<<x<<' '<<y<<endl;
            }
            else
            {
                double k1, k2, b1, b2, x, y;
                k1 = (y2 - y1) / (x2 - x1);
                k2 = (y4 - y3) / (x4 - x3);
                b1 = (x2 * y1 - x1 * y2) / (x2 - x1);
                b2 = (x4 * y3 - x3 * y4) / (x4 - x3);
                if(k1 == k2)//斜率相同则进行进一步的判断
                {
                    if(b1 == b2)//重合
                        printf("LINE
    ");
                    else if(b1 != b2)
                        printf("NONE
    ");
                }
                else
                {
                    x = (b2 - b1) / (k1 - k2);
                    y = k1 * x + b1;
                    printf("POINT ");
                    printf("%.2lf %.2lf
    ",x, y);
                }
            }
        }
    	printf("END OF OUTPUT
    ");
        return 0;
    }
    
    
    

    J - The Suspects

    题意:

    0号同学有带病毒的嫌疑,给你n个团体,如果团体内有一个人有嫌疑,则整个团体都有嫌疑,问有多少带病毒嫌疑的人

    思路:

    并查集的变形,因为0号有嫌疑,最后要求所有带病毒嫌疑的人,就可以利用并查集合并并查找0号个体所在的团体,最终输出该团体的人数即可。

    又因为是0号有嫌疑,是最小的一号,我们就可以采用取小原则,而非之前的靠左原则,即合并的时候谁的号小就归顺谁,这样能保证头领是0号,然后查找的时候只需要找他的头领是不是0号即可!

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 30000 + 5;
    int n, m, k, tr[N];//
    void init()//初始化数组
    {
        for(int i = 0; i < n; ++i)
        {
            tr[i] = i;
        }
    }
    int find1(int v)//俗称找爹函数
    {
        if(f[v] == v)//找到了就返回爹
            return v;
        else//没找到就继续找爹
        {
            f[v] = find1(f[v]);
            return f[v];
        }
    }
    void ranran(int u, int v)//合并
    {
        int x = find1(u);//先把x,y的爹找出来
        int y = find1(v);
        if(x < y)//取小原则
            f[y] = x;
        else if(x > y)
            f[x] = y;
    }
    int a, b, c, sum;//sum用来计数,记得要清0
    int main()
    {
        while(cin>>n>>m)
        {
            if(n == 0 && m == 0)//输入都是0的时候结束程序
                break;
            init();//初始化
            sum = 0;//清0
            while(m--)//m组例子
            {
                cin>>a>>b;
                a--;//a个数,而b就是a个数中的一个,所以写while之前得让a--
                while(a--)
                {
                    cin>>c;
                    ranran(b, c);//每一次都A1与Ai进行并查集操作
                }
            }
            for(int i = 0; i < n; i++)
            {
                if(f[i] == 0 || find1(i) == 0)//两种情况,一种是本身就是0,另一种是认的爹是0
                    sum++;
            }
            cout<<sum<<endl;
        }
        return 0;
    }
    

    B - String LCM

    题意:

    给你n对字符串,让你判断两个串的“最小公倍数”,如:abab 与 ab的最小公倍数是abab,如果没有的话就输出-1

    代码如下,具体思路我会单独开一个博客写

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <stdlib.h>
    #include <sstream>
    #include <map>
    #include <set>
    using namespace std;
    int next1[100005],next2[100005], len1, len2;
    string s1, s2;
    void getnext1()
    {
        int j, k;
        j = 0;
        k = -1;
        next1[0] = -1;
        while(j < len1)
        {
            if(k == -1 || s1[j] == s1[k])
                next1[++j] = ++k;
            else
                k = next1[k];
        }
    }
    void getnext2()
    {
        int j, k;
        j = 0;
        k = -1;
        next2[0] = -1;
        while(j < len2)
        {
            if(k == -1 || s2[j] == s2[k])
                next2[++j] = ++k;
            else
                k = next2[k];
        }
    }
    int gcd(int a, int b)
    {
        if(b) return gcd(b, a % b);
        else return a;
    }
    int main()
    {
        int n, a, b, k;
       // string s1, s2;
        cin>>n;
        while(n--)
        {
            memset(next1, 0, sizeof(next1));
            memset(next2, 0, sizeof(next2));
            k = 1;
            cin>>s1>>s2;
            len1 = s1.size();
            len2 = s2.size();
            getnext1();
            getnext2();
            a = len1 - next1[len1];
            b = len2 - next2[len2];
            if(len1 % a != 0)
                a = len1;
            if(len2 % b != 0)
                b = len2;
    //        for(int i = 1; i <= len1; i++)
    //            cout<<next1[i]<<' ';
     //       cout<<len1 - next1[len1 - 1]<<endl<<len2 - next2[len2 - 1];
            if(a != b || len1 % a != 0 || len2 % b != 0)
            {
    //            cout<<a<<' '<<b<<endl<<len1<<' '<<len2;
    //            cout<<"第一处"<<endl;
                cout<<-1;
            }
            else
            {
                for(int i = 0; i < a; i++)
                {
                    if(s1[i] != s2[i])
                    {
                        k = 0;
                        break;
                    }
                }
                if(k == 0)
                {
                    cout<<-1;
                }
                else
                {
                    int c = (len1 / a * len2 / b) / gcd(len1 / a, len2 / b);
                    while(c--)
                    {
                        for(int i = 0; i < a; i++)
                            cout<<s1[i];
                    }
                }
            }
            cout<<endl;
        }
        return 0;
    }
    
    

    总结:

    A题:思维题,最少四个因子,代表你只需要找两个因子即可,再仔细分析一下就知道是找素数因子

    B题: KMP的next数组的思维题(我是真没想到是KMP的题.jpg

    D题:01字符串的问题,题干很长,看似很难,其实不难,就是第一位一定是1,然后下面每一位都判断选1的时候加起来和上一位一不一样,不一样就选1,一样就选0

    G题:线段树区间查询区间修改,套板子即可

    H题:贪心,每次从第一个系统开始判断,如果能拦住就更新一下,拦不住就再找一个系统,最后输出总系统的个数

    I题:并查集板子

    J题:并查集的升级版,根据题意改一下find函数,将以左为尊改为以小为尊

    M题:给你四个点,前两个点确定一条直线,后两个点确定一条直线,问你这两条直线的位置关系,题目不难,就是细节比较多,判斜率不存在和斜率存在,在分别判是平行还是重合还是相交,特别注意不要忘了判两个直线都没斜率的时候是否重合,剩下的的就考的是数学知识,推个公式即可

    不是所有的牛奶都叫特仑苏,也不是所有的人都叫猪猪
  • 相关阅读:
    广度遍历有向图
    坚持的力量 第二十一篇
    坚持的力量 第二十二篇
    搜索引擎首页
    安装ubuntu
    最小生成树之Kruskal算法
    最小生成树之PRIM算法
    文件同步软件
    [恢]hdu 2151
    [恢]hdu 1396
  • 原文地址:https://www.cnblogs.com/chelsea0901/p/14313082.html
Copyright © 2020-2023  润新知