Pinely Round 4 (Div. 1 + Div. 2)

题目链接:Pinely Round 4 (Div. 1 + Div. 2)

总结:被B卡了一年。

A. Maximize the Last Element

tag:模拟

Description:给定一个长度为奇数的数组,每次可以删除两个相邻的元素,直到剩下一个元素为止,求该元素的最大值。

Solution:模拟发现之后保留奇数位的值。

void solve(){
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i ++)
        cin >> a[i];
    
    int ans = 0;
    for (int i = 0; i < n; i += 2)
        ans = max(ans, a[i]);

    cout << ans << endl;
}

B. AND Reconstruction

tag:构造

Description:给定一个长为 n − 1 n - 1 n1的数组 b b b,构造一个长度为 n n n的数组 a a a,满足 a i & a i + 1 = = b i a_i \& a_{i + 1} == b_i ai&ai+1==bi,否则输出 − 1 -1 1

Solution:我们令 a 1 = = b 1 a_1 == b1 a1==b1。然后我们发现 a i a_i ai会影响 b i b_i bi b i − 1 b_{i - 1} bi1,那么 a i a_i ai包含最少的 1 1 1时,必须为 b i − 1 ∣ b i b_{i - 1} | b_i bi1bi。最后我们令 a n = = b n − 1 a_n == b_{n - 1} an==bn1

Competing:一开始就想到了,但是思路不清晰,气。

void solve(){
    cin >> n;
    vector<int> b(n), a(n);
    for (int i = 0; i < n - 1; i ++){
        cin >> b[i];
    }
    a[0] = b[0];
    for (int i = 1; i < n - 1; i ++){
        a[i] = b[i] | b[i - 1];
    }
    a[n - 1] = b[n - 2];
    for (int i = 0; i < n - 1; i ++){
        if (b[i] != (a[i] & a[i + 1])){
            cout << -1 << endl;
            return;
        }
    }

    for (int i = 0; i < n; i ++)
        cout << a[i] << " \n"[i + 1 == n];
}

C. Absolute Zero

tag:构造

Description:给定一个长度为 n n n的数组,每次操作可以选定一个 x x x,将 a i a_i ai更新为 ∣ a i − x ∣ |a_i - x| aix。构造一个操作序列不操作 40 40 40次,使数组 a a a全为 0 0 0.

Solution:模拟几组样例发现,如果数组中所有元素的奇偶性必须相同,否则当一个数变为奇数,另一个数会变成偶数。(开始时这两个数奇偶性不同)。

  • 我们只需要每次减去 ( r + l ) / 2 (r + l) / 2 (r+l)/2,逐渐缩小上界即可。
  • 可以使用 2 31 , 2 30 , . . . 1 2^{31}, 2^{30}, ... 1 231,230,...1。逐渐缩小上界。

Competing:将 r + l r +l r+l写为 r − l r - l rl

void solve(){
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i ++){
        cin >> a[i];
    }
    sort(a.begin(), a.end());
    for (int i = 1; i < n; i ++){
        if ((a[i] - a[i - 1]) & 1){
            cout << -1 << endl;
            return;
        }
    }
    if (a[0] == a[n - 1]){
        if (a[0] == 0){
            cout << 0 << endl << endl;
            return;
        }
        cout << 1 << endl;
        cout << a[0] << endl;
        return;
    }

    vector<int> ans;
    while (a[0] != a[n - 1]){
        int t = (a[n - 1] + a[0]) / 2;
        ans.eb(t);
        for (int i = 0; i < n; i ++){
            a[i] = abs(a[i] - t);
        }
        sort(a.begin(), a.end());
    }
    if (a[0] != 0)
        ans.eb(a[0]);
    cout << ans.size() << endl;
    for (int i = 0; i < ans.size(); i ++)
        cout << ans[i] << " \n"[i + 1 == ans.size()];

}

D. Prime XOR Coloring

tag:构造

四色定理:“将平面任意地细分为不相重叠的区域,每一个区域总可以用1234这四个数字之一来标记而不会使相邻的两个区域得到相同的数字。”这里所指的相邻区域是指有一整段边界是公共的。如果两个区域只相遇于一点或有限多点就不叫相邻的。

Description:有 n n n个顶点,编号从 1 1 1 n n n,当 u ⊕ v u \oplus v uv为质数时, u u u v v v之间有一条边。用最少的颜色给所有顶点染色,要求相邻顶点的颜色不能相同。

Solution:考虑哪些点之间连边太难,考虑哪些点之间不连边。

  • 根据四色定理知,当 n > = 6 n >= 6 n>=6时,一定需要四种颜色。
  • 考虑所有质数,发现除了 2 2 2都是奇数。 2 2 2的二进制 01 01 01 3 3 3的二进制 10 10 10 4 4 4的二进制 00 00 00。那么我们对 4 4 4取余, m o d 4 mod4 mod4相同的数异或之后一定不是质数。
  • 然后特判 n < = 5 n <= 5 n<=5
void solve(){
    cin >> n;
    if (n == 1){
        cout << 1 << endl << 1 << endl;
    }
    else if (n == 2){
        cout << 2 << endl;
        cout << 1 << " " << 2 << endl;
    }
    else if (n == 3){
        cout << 2 << endl;
        cout << "1 2 2\n";
    }
    else if (n == 4){
        cout << 3 << endl;
        cout << "1 2 2 3\n";
    }
    else if (n == 5){
        cout << 3 << endl;
        cout << "1 2 2 3 3\n";
    }
    else{
        cout << 4 << endl;
        for (int i = 1; i <= n; i ++){
            cout << (i % 4) + 1 << " \n"[i == n];
        }
    }
}

E. Coloring Game

tag:二分图 + 交互

Description:给定一个图,一共 n n n轮操作,每轮操作:

  • A l i c e Alice Alice选择三个颜色中选择两个颜色( 1 , 2 , 3 1, 2, 3 1,2,3)。
  • B o b Bob Bob选择一个未染色的顶点进行染色。
  • 如果存在相邻顶点颜色相同 A l i c e Alice Alice赢,否则 B o b Bob Bob赢。
  • 你需要选择玩家,并给出获胜策略。

Solution:显然 A l i c e Alice Alice想赢每次给出两个相同的颜色最好。模拟一下发现如果是二分图则 B o b Bob Bob必赢,否则 A l i c e Alice Alice必赢。

  • 如果不是二分图, A l i c e Alice Alice获胜,每次输出 1 , 2 1, 2 1,2即可。
  • 如果是二分图 B o b Bob Bob获胜,先用 1 1 1或者 2 2 2将其中一种颜色染完,剩下的一种用 3 3 3进行染色。因为 1 1 1的个数加 2 2 2的个数等于 n n n,因此必定有一种颜色会被染完。
void solve(){
    cin >> n >> m;
    vector g(n, vector<int>());
    vector st(n, -1);

    for (int i = 0; i < m; i ++){
        int x, y;
        cin >> x >> y;
        x --, y --;
        g[x].eb(y);
        g[y].eb(x);
    }
    
    bool flag = true;
    vector<int> f1, f2;
    auto dfs = [&](auto self, int x, int fa, int c) -> void {
        st[x] = c;
        if (c == 1){
            f1.eb(x);
        }
        else
            f2.eb(x);
        for (auto i : g[x]){
            if (i == fa)
                continue;
            if (st[i] == -1){
                self(self, i, x, !c);
            }
            else{
                if (st[i] == c){
                    flag = false;
                }
            }
        }
    };
    dfs(dfs, 0, -1, 1);
    if (flag){
        cout << "Bob" << endl;
        int s1 = f1.size(), s2 = f2.size();
        for (int i = 0; i < n; i ++){
            int a, b;
            cin >> a >> b;
            if (a > b)
                swap(a, b);
            if (a == 1){
                if (s1){
                    s1 --;
                    cout << f1.back() + 1 << " " << a << endl;
                    f1.pop_back();
                }
                else{
                    s2 --;
                    cout << f2.back() + 1 << " " << b <<  endl; 
                    f2.pop_back();
                }
            }
            if (a == 2){
                if (s2){
                    s2 --;
                    cout << f2.back() + 1 << " " << a <<  endl;
                    f2.pop_back();
                }
                else{
                    s1 --;
                    cout << f1.back() + 1 << " " << b << endl;
                    f1.pop_back();
                }
            }
        }
    }
    else{
        cout << "Alice" << endl;
        for (int i = 1; i <= n; i ++){
            int a, b;
            cout << "1 2" << endl;
            cin >> a >> b;
        }
    }
}

F. Triangle Formation

tag:大氛围结论 + 小范围暴力

Description:给定 n n n根木棍,每根木棍的长度为 a i a_i ai。有 q q q次询问,每次询问给定 l , r l, r l,r。判断每次询问是否能选出 6 6 6根木棍组成两个三角形。 l < = i < = r , r − l + 1 > = 6 , 1 < = a i < = 1 0 9 , 1 < = q < = 1 0 5 l <= i <= r, r - l + 1 >= 6, 1 <= a_i <= 10^9, 1 <= q <= 10^5 l<=i<=r,rl+1>=6,1<=ai<=109,1<=q<=105

Solution:最长的不能组成三角形的序列:斐波拉契序列,因为 a 1 + a 2 = = a 3 a_1 + a_2 == a3 a1+a2==a3,那么 a 1 + a 2 < = a 4 a_1 + a_2 <= a4 a1+a2<=a4。我们尽可能维护前置条件。

  • 根据 a i < = 1 e 9 a_i <= 1e9 ai<=1e9,我们得到当 F 48 F_{48} F48时,最大。因此序列长度超过 48 48 48时,一定能够组成三角形。我们令区间长度为 60 60 60,那么当 r − l > = 60 r - l >= 60 rl>=60时,一定有解。

  • 考虑 r − l < = 60 r - l <= 60 rl<=60,我们先将区间内的数排序。要想三边构成三角形,那么三条边要即可能接近。如给定 a , b , c a, b, c a,b,c,需要满足 a + b < = c a + b <= c a+b<=c,假设 b b b固定,那么 a a a要尽可能大, c c c要尽快能小。因此三条边越接近越好。如果能从区间内找到两个无交集的三角形区间即符合条件。

  • 考虑连续 6 6 6个数组成两个三角形,假设现在有 7 7 7个数 1 , 2 , 3 , 4 , 5 , 6 , 7 1, 2, 3, 4, 5, 6, 7 1,2,3,4,5,6,7。第一个三角形为 1 , 6 , 7 {1, 6, 7} 1,6,7,那么第二个三角形为 2 , 3 , 4 或 3 , 4 , 5 {2, 3, 4}或{3,4,5} 2,3,43,4,5。如果为 2 , 3 , 4 {2, 3, 4} 2,3,4,那么 1 , 6 , 7 {1, 6, 7} 1,6,7不如 5 , 6 , 7 {5, 6, 7} 5,6,7。同理如果为 3 , 4 , 5 {3, 4, 5} 3,4,5,那么 1 , 6 , 7 {1,6,7} 1,6,7不如 2 , 6 , 7 {2, 6, 7} 2,6,7

  • 假设现在有 a , b , c , d , e , f a, b, c, d, e, f a,b,c,d,e,f六条边。其中 f f f一定是长边, a , b a, b a,b一定是短边。

    • 如果 c c c是长边,则 a + b > c , d + e > f a + b > c, d + e > f a+b>c,d+e>f,已讨论。
    • 如果 d d d是长边,则 a + b > d , c + e > f ; a + c > d , b + e > f ; b + c > d , a + e > f a + b > d, c + e > f; a + c > d, b + e > f; b + c > d, a + e > f a+b>d,c+e>f;a+c>d,b+e>f;b+c>d,a+e>f,三种情况。
    • 如果 e e e是长边,则 a + b > e , c + d > f ; a + c > e , b + d > f ; a + d > e , b + c > f ; b + c > e , a + d > f a + b > e, c + d > f; a + c > e, b + d > f;a + d > e, b + c > f;b + c > e, a + d > f a+b>e,c+d>f;a+c>e,b+d>f;a+d>e,b+c>f;b+c>e,a+d>f,四种情况。
void solve(){
    int q;
    cin >> n >> q;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i ++)
        cin >> a[i];
    
    while (q --){
        int l, r;
        cin >> l >> r;
        if (r - l >= 60){
            cout << "YES\n";
        }
        else{
            vector<int> b;
            for (int i = l; i <= r; i ++)
                b.eb(a[i]);
            sort(b.begin(), b.end());
            int len = b.size();
            
            int mi = -1, ma = -1;
            for (int i = 1; i + 1 < len; i ++){
                if (b[i - 1] + b[i] > b[i + 1]){
                    if (mi == -1){
                        mi = i;
                        continue;
                    }
                    ma = i;
                }
            }
            if (ma - mi >= 3){
                cout << "YES\n";
            }
            else{
                bool ok = false;
                for (int i = 0; i + 5 < len; i ++){
                    vector<int> c(6);
                    for (int j = 0; j < 6; j ++)
                        c[j] = b[i + j];
                    if (c[0] + c[1] > c[3] && c[2] + c[4] > c[5]){
                        ok = true;
                        break;
                    }
                    else if (c[0] + c[2] > c[3] && c[1] + c[4] > c[5]){
                        ok = true;
                        break;
                    }
                    else if (c[1] + c[2] > c[3] && c[0] + c[4] > c[5]){
                        ok = true;
                        break;
                    }
                    else if (c[0] + c[1] > c[4] && c[2] + c[3] > c[5]){
                        ok = true;
                        break;
                    }
                    else if (c[0] + c[2] > c[4] && c[1] + c[3] > c[5]){
                        ok = true;
                        break;
                    }
                    else if (c[0] + c[3] > c[4] && c[1] + c[2] > c[5]){
                        ok = true;
                        break;
                    }
                    else if (c[1] + c[2] > c[4] && c[0] + c[3] > c[5]){
                        ok = true;
                        break;
                    }
                }
                if (ok)
                    cout << "YES\n";
                else
                    cout << "NO\n";
            }
        }
    }
}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值