CF1994D-鸽巢原理

CF1994D-鸽巢原理

大致题意

Vanya 有一个图,图中有 n 个顶点(编号从 1 到 n )和 a 个由 n 个整数组成的数组;最初,图中没有边。万尼亚觉得无聊,为了找点乐子,他决定进行 n−1 次运算。

操作数 x (操作数从 1 开始依次编号)如下:

  • 选择 2 个不同的数 1≤u,v≤n ,使得|auav|可以被 x 整除。
  • 在顶点 u 和 v 之间添加一条无向边。

使用 n−1 运算帮助Vanya得到一个连通的图,或者确定这是不可能的。

  • 如果沿着边可以从任何顶点到达其他顶点,那么这个图就叫做连通图。

解题思路

首先介绍鸽巢原理

鸽巢原理:将n+1个物品划分为n组,那么至少有一组有两个(及以上)的物品

推广:将n个物品划分为k组,那么至少有一组有nk个物品

对于本题,我们考虑操作的条件:|auav|,对于两个数如果他们对同一个数字取模余数相同,那么就会满足该条件

于是我们可以考虑将所有数字对操作数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;
}
posted @   empty_y  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示