D. Funny Game (Codeforces Round 959)

题意

给定 n n n个点,第 i i i个点的值为 a i ( 1 ≤ a i ≤ n ) a_i(1 \le a_i \le n) ai(1ain),给定 n − 1 n-1 n1次操作,第 x x x ( 1 ≤ x ≤ n − 1 ) (1 \le x \le n-1) (1xn1)操作选择两个点 a u a_u au a v a_v av,如果 x ∣ a b s ( a u − a v ) x |abs(a_u-a_v) xabs(auav)则可以将这两个点连起来,问 n − 1 n-1 n1次操作后是否能连接所有的点并输出每条边

思路

  • 转化条件: x ∣ a b s ( a u − a v ) x |abs(a_u-a_v) xabs(auav),即 a u ≡ a v ( m o d   x ) a_u\equiv a_v(mod \ x) auav(mod x),那么每次连边的时候只要找两个模x结果相同的数即可
  • 抽屉原理:当进行最后一次即第 n − 1 n-1 n1次操作时,由于一共有 n n n个数且这 n n n个数的范围为1~n,那么每个数模 n − 1 n-1 n1后最多有 n − 1 n-1 n1个结果 ( 0 , 1 , . . . , n − 3 , n − 2 ) (0,1,...,n-3,n-2) (0,1,...,n3,n2) n n n个数, n − 1 n-1 n1个结果,说明至少有两个数他们模 n − 1 n-1 n1的结果是一样的
  • 于是,从第 n − 1 n-1 n1次操作开始往前枚举,每次连接模 x x x的结果相同的两个点即可,当然已经属于同一个连通块的两个点是不能连接的,可以使用并查集来判断是否属于同一个连通块

代码

/* \/\ \         /\ \                        
\ \ \/'/'  __  __\ \ \         __    ___     
 \ \ , <  /\ \/\ \\ \ \  __  /'__`\/' _ `\   
  \ \ \\`\\ \ \_\ \\ \ \L\ \/\  __//\ \/\ \  
   \ \_\ \_\/`____ \\ \____/\ \____\ \_\ \_\ 
    \/_/\/_/`/___/> \\/___/  \/____/\/_/\/_/ 
               /\___/                        
       ___ ___ \/__/      qq: 2729269812     
     /' __` __`\       email: kkkylen@qq.com 
     /\ \/\ \/\ \    ___   __  __            
     \ \ \ \ \ \ \  / __`\/\ \/\ \           
      \ \ \ \ \ \ \/\ \L\ \ \ \_\ \          
       \ \ \ \ \ \ \ \____/\ \____/          
        \/_/\/_/\/_/\/___/  \/__*/           
#include<bits/stdc++.h>
#define x first
#define y second
#define endl '\n'
#define int long long
using namespace std;

void KyLen() {
    int n;
    cin >> n;
    vector<int> a(n+1), p(n+1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        p[i] = i;
    }
    function<int(int)> find = [&] (int x) {
        return p[x] == x ? x : p[x] = find(p[x]);
    };
    auto merge = [&] (int a, int b) {
        p[find(a)] = find(b);
    };
    vector<pair<int,int>> ans;
    for (int mod = n - 1; mod >= 1; mod--) {
        map<int,int> m;
        for (int i = 1; i <= n; i++) {
            int cur = m[a[i] % mod];
            if (cur == 0) {
                m[a[i] % mod] = i;
            } else if (find(cur) != find(i)) {
                merge(cur,i);
                ans.push_back({cur,i});
                break;
            }
        }
    }
    cout << "YES" << endl;
    for (auto it = ans.rbegin(); it != ans.rend(); it++) {
        cout << it -> x << ' ' << it -> y << endl;
    }
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T; cin >> T; while(T--) KyLen();
    return 0;
}
  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KyLenMou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值