2020牛客暑期多校训练营(第三场)


A. Clam and Fish

https://ac.nowcoder.com/acm/contest/5668/A
有鱼的时候就捕鱼,没有鱼有蛤蜊时做鱼食,
既没有鱼也没有蛤蜊时如果有鱼食就钓鱼,
最后,如果还有鱼食剩余,拿出剩余本来是做鱼食的时间,改为钓鱼

#include <bits/stdc++.h>
using namespace std;
int n;
string s;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int T;
    cin >> T;
    while (T--) {
        cin >> n >> s;
        int res = 0, cnt = 0;
        for (int i = 0; i < n; i++) {
            if (s[i] == '2' || s[i] == '3') res++;
            else if (s[i] == '1') cnt++;
            else if (s[i] == '0' && cnt) {
                res++;
                cnt--;
            }
        }
        cout << res + (cnt / 2) << endl;
    }
    return 0;
}

B. Classical String Problem

https://ac.nowcoder.com/acm/contest/5668/B

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 3e6 + 5;
char s[MAXN], op[5];

int main() {
    int q, n;
    scanf("%s", s);
    scanf("%d", &q);
    int cnt = 0, len = strlen(s);
    while (q--) {
        scanf("%s%d", op, &n);
        if (op[0] == 'A') {
            printf("%c\n", s[(cnt + n - 1) % len]);
        } else {
            cnt = (cnt + n + len) % len;
        }
    }
    return 0;
}

C. Operation Love · 几何

https://ac.nowcoder.com/acm/contest/5668/C
识别左右手

方法1:将旋转的图形掰正

几个知识点:

  1. 直线的斜率可以通过 c++函数 a t a n 2 ( Δ y , Δ x ) atan2(\Delta y,\Delta x) atan2(Δy,Δx) 求出,其返回的是弧度,即当斜率为90°时
    a t a n 2 ( 90 ° ) = 1.57 atan2(90°)=1.57 atan2(90°)=1.57
  2. c++的 sin ⁡ ( x ) \sin(x) sin(x) cos ⁡ ( x ) \cos(x) cos(x) 这些函数都是使用弧度制的
  3. ( x p , y p ) (x_p,y_p) (xp,yp) 绕某一点 ( x o , y o ) (x_o,y_o) (xo,yo) 旋转 顺时针旋转θ角度后的新坐标
    x = ( x p − x o ) cos ⁡ θ − ( y p − y o ) sin ⁡ θ + x o y = ( y p − y o ) cos ⁡ θ + ( x p − x o ) sin ⁡ θ + y o x=(x_p-x_o)\cos\theta-(y_p-y_o)\sin\theta+x_o\\ y=(y_p-y_o)\cos\theta+(x_p-x_o)\sin\theta+y_o x=(xpxo)cosθ(ypyo)sinθ+xoy=(ypyo)cosθ+(xpxo)sinθ+yo
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-6; // 坑 1e-8就过不去
const double pi = acos(-1.0);
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    else return 1;
}
//判断小数和0是否等于

struct Point {
    double x, y;

    Point() {}

    Point(double _x, double _y) { x = _x, y = _y; }

    void input() { scanf("%lf%lf", &x, &y); }//输入
    void output() { printf("%.2f %.2f\n", x, y); } //输出

    Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }

    Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); }

    Point operator*(const double &k) const { return Point(x * k, y * k); }

    Point operator/(const double &k) const { return Point(x / k, y / k); }

    bool operator==(Point b) const { return sgn(x - b.x) == 0 && sgn(y - b.y) == 0; }

    bool operator<(Point b) const { return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x; }

    double operator^(const Point &b) const { return x * b.y - y * b.x; }
    //叉积

    double operator*(const Point &b) const { return x * b.x + y * b.y; }
    //点积

    double distance(Point p) { return hypot(x - p.x, y - p.y); }
    //两点之间的距离
};

struct Line {
    Point s, e;

    Line() {}

    Line(Point _s, Point _e) {
        s = _s;
        e = _e;
    }

    bool operator==(Line v) { return (s == v.s) && (e == v.e); }

    double angle() {
        double k = atan2(e.y - s.y, e.x - s.x);
        if (sgn(k) < 0)k += pi;
        if (sgn(k - pi) == 0) k -= pi;
        return k;
    }//返回直线倾斜角 0<=angle<pi


    int relation(Point p) {
        int c = sgn((p - s) ^ (e - s));
        if (c < 0) return 1; //点在直线的左侧
        else if (c > 0) return 2;//点在直线的右侧
        else return 3;//点在直线上
    }//判断点与直线的关系
};

Point p[25], tmp;
Line L1, L2;
int u1, v1, u2, v2;

int main() {
    
    int T;
    scanf("%d", &T);
    while (T--) {
        for (int i = 0; i < 20; i++)  p[i].input();

        for (int i = 0, j = 19; i < 20; i++, j = i - 1) {
            if (sgn(9.0 - p[i].distance(p[j])) == 0) { // 找到最长的边 作为将来的x轴
                if (p[i].x > p[j].x) { 
                    L2 = {p[j], p[i]};
                    u2 = j, v2 = i;
                } else {
                    L2 = {p[i], p[j]};
                    u2 = i, v2 = j;
                }
                break;
            }
        }

        //绕较小的端点u2点旋转
        double angle = L2.angle();
        for (int i = 0; i < 20; i++) {
            tmp.x = (p[i].x - p[u2].x) * cos(angle) - (p[i].y - p[u2].y) * sin(angle) + p[u2].x;
            tmp.y = (p[i].y - p[u2].y) * cos(angle) + (p[i].x - p[u2].x) * sin(angle) + p[u2].y;
            p[i] = tmp;
        }

        for (int i = 0, j = 19; i < 20; i++, j = i - 1) {
            if (sgn(6.0 - p[i].distance(p[j])) == 0) { // 将边长为6的那条边作为y轴
                if (p[i].y > p[j].y) L1 = {p[j], p[i]};
                else L1 = {p[i], p[j]};

                u1 = i, v1 = j;
                break;
            }
        }

        if (p[u2].x > p[v2].x) L2 = {p[v2], p[u2]}; // 根据新生成的坐标生成x轴
        else L2 = {p[u2], p[v2]};

        int f = 0;
        for (int i = 0; i < 20; i++) {
            if (i != u1 && i != v1 && i != u2 && i != v2) {
                int k1 = L1.relation(p[i]);//判断直线与点的位置关系
                int k2 = L2.relation(p[i]);

                if (k1 == 2 && k2 == 1) f = 0; // 右手
                else if (k1 == 1 && k2 == 1) f = 1; // 左手
               	else if (k1 == 1 && k2 == 2) f = 0;
                else if (k1 == 2 && k2 == 2) f = 1;
                
                break;
            }
        }

        if (f) {
            puts("left");
        } else {
            puts("right");
        }

    }
    return 0;
}

方法2:叉积
选择长度为6的边和长度为9的边作为两个向量,
用右手判断,四指握拳方向与向量6指向向量9的方向一致,
大拇指朝远离自己的方向即为右手,朝向自己即为左手

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-5;

int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    else return 1;
}
//判断小数和0是否等于

struct Point {
    double x, y;

    Point() {}

    Point(double _x, double _y) { x = _x, y = _y; }

    void input() { scanf("%lf%lf", &x, &y); }//输入
    void output() { printf("%.2f %.2f\n", x, y); } //输出

    Point operator - (const Point &b) const { return Point(x - b.x, y - b.y); }

    Point operator + (Point b) { return Point(x + b.x, y + b.y); }
    
    double operator ^ (const Point &b) const { return x * b.y - y * b.x; }
    //叉积

    double distance(Point p) { return hypot(x - p.x, y - p.y); }
    //两点之间的距离
};

typedef Point Vector;

Point p[25];
Vector v6, v9;

int main() {

    int T;
    scanf("%d", &T);
    while (T--) {
        for (int i = 0; i < 20; i++) {
            p[i].input();
        }

        int a, b;
        for (int i = 0, j = 19; i < 20; i++, j = i - 1) {
            if (sgn(9.0 - p[i].distance(p[j])) == 0) {
                a = i, b = j;
                break;
            }
        }

        for (int i = 0, j = 19; i < 20; i++, j = i - 1) {
            if (sgn(6.0 - p[i].distance(p[j])) == 0) {
                if (i == a) {//两条边的交点为点 p[i]
                    v6 = p[j] - p[i];
                    v9 = p[b] - p[i];
                } else if (i == b) {
                    v6 = p[j] - p[i];
                    v9 = p[a] - p[i];
                } else if (j == a) {//交点为 p[j]
                    v6 = p[i] - p[j];
                    v9 = p[b] - p[j];
                } else {// j==b
                    v6 = p[i] - p[j];
                    v9 = p[a] - p[j];
                }
                break;
            }
        }

        if (sgn(v6 ^ v9) < 0) {
            puts("right");
        } else { // 不会有=0
            puts("left");
        }
    }
    return 0;
}

D. Points Construction Problem

https://ac.nowcoder.com/acm/contest/5668/D

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 1e3 + 10;

int n, m, k;
pii res[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int T;
    cin >> T;
    while (T--) {
        cin >> n >> m;
        if ((m & 1) || m > 4 * n) {
            cout << "No" << endl;
            continue;
        }

        /*
         * 显然 m<=4n
         *
         * 假设所有的黑子都在一个矩形中 a行b列
         * 不管是塞满还是没塞满 至少可以提供 2(a+b)对
         * 所以 在 n<=ab 的前提下 m>= 2(a+b)
         * 暴力枚举所有 a+b 查看m是否在范围内
         * */
        int a = 1, b = 1;
        while (a * b < n) {
            if (a < b)a++;
            else b++;
        }
        
        // 矩形内部的黑子最多的情况下 m还比当前能够匹配的对数少 显然是不可能的了
        if (2 * (a + b) > m) {
            cout << "No" << endl;
            continue;
        }

        int cnt = 2 * (a + b);
        int id = 0;//定点最后一个黑子的位置
        int f = 0;
        for (int i = 1; i <= a; i++) {
            for (int j = 1; j <= b; j++) {
                res[++id] = {i, j};
                if (id == n) {
                    f = 1;
                    break;
                }
            }
            if (f) break;
        }

        // 对数不够时 移动矩形里的黑子
        // 从 (a+1,b+1) 开始沿斜线摆放
        int x = a + 1, y = b + 1;
        while (cnt < m) {
            // 如果在边缘 每次移动 只会增加2对
            if (res[id].first == 1 || res[id].second == 1)
                cnt += 2;
            else cnt += 4;//否则每次都有4对增加

            res[id--] = {x, y};
            x++;
            y++;

        }
        id++;

        // 如果一不小心增过头了 只可能多了2个
        // 就移动黑子到另一个也被移出去的黑子 相邻的位置
        if (cnt == m + 2) {
            //只往外移动了一个 没有共患难的黑子
            // 就移动到 矩形边缘的一个黑子的旁边
            if (id == n) {
                res[id] = {1, 0};
            } else {
                res[id] = {res[id + 1].first, res[id + 1].second + 1};
            }
        }

        cout << "Yes" << endl;
        for (int i = 1; i <= n; i++) {
            cout << res[i].first << " " << res[i].second << endl;
        }
    }
    return 0;
}

E. Two Matchings · dp

https://ac.nowcoder.com/acm/contest/5668/E

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;//inf=(1ll<<60)
const int N = 1e6 + 10;
ll a[N];
ll dp[N];
int n;


int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int T;
    cin >> T;
    while (T--) {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        sort(a + 1, a + 1 + n);

        ll res = 0;
        for (int i = 2; i <= n; i += 2) {
            res += a[i] - a[i - 1];
        }

        for (int i = 0; i <= n; i++) {
            dp[i] = inf;
        }

        dp[4] = a[4] + a[3] - a[2] - a[1]; //  当匹配对数有偶数对时 显然(a[n-3],a[n-1]) (a[n-2],a[n]) 更优
        if (n >= 6) //  当匹配对数有奇数对时 需要让其中三组配对成 (a[n-1],a[n-3]) (a[n-2],a[n-5]) (a[n-4],a[n]) dp找最优解
            dp[6] = a[6] - a[4] + a[5] - a[2] + a[3] - a[1];
        for (int i = 8; i <= n; i += 2) {
            dp[i] = min(dp[i - 4] + a[i] - a[i - 2] + a[i - 1] - a[i - 3],
                        dp[i - 6] + a[i] - a[i - 2] + a[i - 1] - a[i - 4] + a[i - 3] - a[i - 5]);
        }
        res += dp[n];

        cout << res << endl;
    }
    return 0;
}

F. Fraction Construction Problem · 构造

https://ac.nowcoder.com/acm/contest/5668/F

给出a,b 构造
c d − e f = a b \cfrac{c}{d}-\cfrac{e}{f}=\cfrac{a}{b} dcfe=ba

分两种情况讨论:

  1. g = gcd ⁡ ( a , b ) ≠ 1 g=\gcd(a,b)\not=1 g=gcd(a,b)=1 时,
    a g + 1 b g − 1 b g = a b \cfrac{\frac{a}{g}+1}{\frac{b}{g}}-\cfrac{1}{\frac{b}{g}}=\cfrac{a}{b} gbga+1gb1=ba
  2. gcd ⁡ ( a , b ) = 1 \gcd(a,b)=1 gcd(a,b)=1 时, c f − d e d f = a b \cfrac{cf-de}{df}=\cfrac{a}{b} dfcfde=ba (此为最简等式),由于a、b互质,如果d、f有相同的因子,则 c f − d e cf-de cfde 肯定可以提取出一个因子,使得上下分子分母通分,这与 a、b互质矛盾,所以满足条件的 b b b 至少有两个及以上的质因数,再利用扩展欧几里得求出c、e即可

题解里提到的 CF1366D

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;

ll exgcd(ll a, ll b, ll &x, ll &y) { // ax+by=gcd(a,b)=1 x与y肯定不同号
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    } else {
        ll g = exgcd(b, a % b, x, y);
        ll tmp = x;
        x = y;
        y = tmp - a / b * y;
        return g;
    }
}

ll a, b;

int primes[N], cnt;     // 存储所有质数
bool vis[N];     // 存储每个数是否已被筛掉

void get_primes(int n) {// 线性筛法求素数
    for (int i = 2; i <= n; i++) {
        if (!vis[i])
            primes[cnt++] = i;
        for (int j = 0; i * primes[j] <= n; j++) {
            vis[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

int main() {
    get_primes(5000);

    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%lld%lld", &a, &b);

        ll g = __gcd(a, b);
        if (g != 1) {// a、b不互质
            a /= g;
            b /= g;
            printf("%lld %lld 1 %lld\n", (a + 1), b, b);//c d e f

        } else {// 互质
            // 因为ab互质 且b=df a=cf-ed 
            // 如果df有相同因子 会使得ab不互质
            ll d = 1;
            for (int i = 0; i < cnt; i++) {
                if (b % primes[i] == 0) {
                    while (b % primes[i] == 0) {
                        d *= primes[i];
                        b /= primes[i];
                    }
                    break;

                }
            }
            ll f = b;
            if (d == 1 || f == 1) {
                printf("-1 -1 -1 -1\n");
                
            } else {
                ll c, e, f = b;
                exgcd(f, d, c, e); //  ax+by=gxd(a,b) 构造 cf-ed=a

                c *= a;
                e *= a;

                if (c < 0) {
                    swap(c, e);
                    swap(d, f);
                }
                e = -e;

                printf("%lld %lld %lld %lld\n", c, d, e, f);
            }
        }
    }
    return 0;
}

G. Operating on a Graph

https://ac.nowcoder.com/acm/contest/5668/G
卡我vector 吐血

#include <bits/stdc++.h>
using namespace std;
const int N = 8e5 + 10;
vector<int> e[N];
list<int> _list[N];
int n, m;
int fa[N];

int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void dfs(int x) {
    for (int i = 1, sz = _list[x].size(); i <= sz; i++) {
        //只取前sz个 链表会在下面的for里添加新的节点 后添加的节点是下次被染色的范围 不应该算进去
        int u = _list[x].front();
        _list[x].pop_front();

       for (int v:e[u]) {
            int fv = find(v);
            if (fv != x) {
                fa[fv] = x;
                _list[x].splice(_list[x].end(), _list[fv]);
                // 将 _list[fv] 的内容插入到 _list[x]末尾  并且删除_list[fv]里的内容
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int T, q, o;
    cin >> T;
    while (T--) {
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
            e[i].clear();
            _list[i].clear();
            _list[i].push_back(i);
        }


        for (int i = 1, u, v; i <= m; i++) {
            cin >> u >> v;
            u++;
            v++;
            e[u].push_back(v);
            e[v].push_back(u);
        }

        cin >> q;

        while (q--) {
            cin >> o;
            o++;
            dfs(o);
        }

        for (int i = 1; i <= n; i++) {
            cout << find(i) - 1 << (i == n ? "\n" : " ");
        }
    }

    return 0;
}

H. Sort the Strings Revision
I. Sorting the Array
J. Operating on the Tree
K. Eleven Game


L. Problem L is the Only Lovely Problem

https://ac.nowcoder.com/acm/contest/5668/L

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
char s[maxn];
int main()
{
    scanf("%s",s);
    int len=strlen(s);
    int flag=1;
    if(s[0]!='l'&&s[0]!='L')flag=0;
    if(s[1]!='o'&&s[1]!='O')flag=0;
    if(s[2]!='v'&&s[2]!='V')flag=0;
    if(s[3]!='e'&&s[3]!='E')flag=0;
    if(s[4]!='l'&&s[4]!='L')flag=0;
    if(s[5]!='y'&&s[5]!='Y')flag=0;
    if(flag)puts("lovely");
    else puts("ugly");
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值