CF1994D-鸽巢原理
CF1994D-鸽巢原理
大致题意
Vanya 有一个图,图中有 n 个顶点(编号从 1 到 n )和 a 个由 n 个整数组成的数组;最初,图中没有边。万尼亚觉得无聊,为了找点乐子,他决定进行 n−1 次运算。
操作数 x (操作数从 1 开始依次编号)如下:
- 选择 2 个不同的数 1≤u,v≤n ,使得可以被 x 整除。
- 在顶点 u 和 v 之间添加一条无向边。
使用 n−1 运算帮助Vanya得到一个连通的图,或者确定这是不可能的。
- 如果沿着边可以从任何顶点到达其他顶点,那么这个图就叫做连通图。
解题思路
首先介绍鸽巢原理
鸽巢原理:将n+1个物品划分为n组,那么至少有一组有两个(及以上)的物品
推广:将n个物品划分为k组,那么至少有一组有个物品
对于本题,我们考虑操作的条件:,对于两个数如果他们对同一个数字取模余数相同,那么就会满足该条件
于是我们可以考虑将所有数字对操作数x取模,由于对于操作数1来说它的可使用边是最多的,而操作n-1使用边是最少的,所以我们优先逆序操作,将所有数字按照对x取余分组
根据鸽巢原理,至少有一组中包含两个或两个以上的数字,那么我们就可以将这两个数字进行连边,连完后需要删除其中一个数。
代码实现
#include<bits/stdc++.h> using namespace std; #define endl "\n" void solve() { int n; cin>>n; vector<int> a(n+1),vis(n+1); vector<pair<int,int>> edg; for(int i = 1;i <= n;++i) cin>>a[i]; for(int i = n-1;i>=1;--i) { vector<vector<int>> vec(i); for(int j = 1;j<=n;++j) { if(!vis[j]) { vec[a[j]%i].push_back(j); } } for(int j = 0;j < i;++j) { if(vec[j].size()>=2) { auto u = vec[j][0],v = vec[j][1]; edg.push_back({u,v}); vis[v] = 1; break; } } } cout<<"YES"<<endl; reverse(edg.begin(),edg.end()); for(auto [u,v]:edg) { cout<<u<<" "<<v<<endl; } } int main() { ios::sync_with_stdio(false); cin.tie(0); int tt; cin>>tt; while(tt--)solve(); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】