2022年江西省大学生程序设计竞赛
比赛链接
2022年江西省大学生程序设计竞赛(正式赛)
C.Graphic Game
给一个 \(2\times n\) 个点,\(m\) 条边,没有重边和自环的无向图,每次可以选择两个度数相等的点删除,求删完所有点的方案
解题思路
鸽笼原理
度数的可能取值有 \(1,\dots,m\),而点数 \(2\times n>m\) 个,即一个无向图最少存在两个点的度数相等,删除两个点后仍然是一个无向图,即该问题一定有解,先存下每个度数对应的节点,考虑从最大的度数开始删除两个数,用 \(set\) 维护即可,\(\color{red}{为什么这样一定能删完?}\)即现在要证明不存在这样一种情况:删除当前度数为 \(t\) 的两个数后,所有度数 \(x<=t\) 的节点个数都不超过 \(1\),但是可能存在度数 \(x>t\) 的节点个数超过 \(1\),可以发现这样的情况很难构造出来,因为对于一个度数 \(x>t\) 的节点来说,说明会有 \(x\) 条边由该节点连向其他节点,假设如果其他点的度数都 \(\leq t\),而有 \(x\) 个这样的点,说明至少存在两个度数相等的点,假设不成立,故与该节点连的点中至少存在一个点的度数 \(>t\),即我们的那种要证明的不存在的情况说明:度数大的点至少也会跟度数大的点连边,且所有其他度数小的节点都不存在相等度数的节点,这样的情况更加难以构造
- 时间复杂度:\(O(nlogn)\)
代码
// Problem: Graphic Game
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/43898/C
// Memory Limit: 524288 MB
// Time Limit: 10000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
// #define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=2e6+5,M=1e6+5;
int n,m,d[N];
int h[N],e[N],ne[N],idx;
set<int>deg[M];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int main()
{
memset(h,-1,sizeof h);
read(n),read(m);
for(int i=1;i<=m;i++)
{
int a,b;
read(a),read(b);
add(a,b),add(b,a);
d[a]++,d[b]++;
}
for(int i=1;i<=2*n;i++)deg[d[i]].insert(i);
int t=m;
while(t>=0)
{
if(deg[t].size()>=2)
{
int x=*deg[t].begin();
deg[t].erase(deg[t].begin());
int y=*deg[t].begin();
deg[t].erase(deg[t].begin());
printf("%d %d\n",x,y);
d[x]=d[y]=0;
for(int i=h[x];~i;i=ne[i])
{
int j=e[i];
if(d[j]>0)
{
deg[d[j]].erase(j);
d[j]--;
deg[d[j]].insert(j);
}
}
for(int i=h[y];~i;i=ne[i])
{
int j=e[i];
if(d[j]>0)
{
deg[d[j]].erase(j);
d[j]--;
deg[d[j]].insert(j);
}
}
}
else
t--;
}
return 0;
}
G.A Game of Taking Numbers
对于 \(n\) 个数 \(a_i\),\(A\) 先手,\(A\) 可以选择任何数,如果 \(A\) 选择了 \(x\),则 \(B\) 选择的 \(y\) 需要满足:\(|x-y|\leq 3\) 或 \(x\equiv y(\bmod 3)\),最后无法操作者败
解题思路
博弈论
如果 \(n\) 为奇数的话 \(B\) 必败,否则将所有对 \(3\)取模的数分成 \(3\) 堆,如果这 \(3\) 堆都为偶数的话 \(A\) 必败,因为无论 \(A\) 从这 \(3\) 堆中哪一堆取数,\(B\) 都可以从相同堆中取数,最后一定是 \(A\) 无法操作,否则剩下 \(奇 \ 奇 \ 偶\) 的情况,同理如果两个奇数堆中存在这样的一对数 \(x,y\) 使得 \(|x-y|\leq 3\),则最后 \(A\) 选到其中一个数时 \(B\) 都可以选择另外一个数,使得 \(A\) 面临 \(偶 \ 偶 \ 偶\) 的必败态,注意还有一种情况需要考虑,如果 \(奇 \ 偶\) 中存在 \(|x-y|\leq 3\) 这样的一对数 \(x,y\),则此时奇偶发生变化,故如果 \(偶\) 中至少存在两个不相等的 \(x\),在两个 \(奇\) 中都存在 \(|x-y|\leq 3\) 这样的一对数 \(x,y\),则无论 \(A\) 选这 \(4\) 个数中的任意一个,\(B\) 都可以选与此对应的数使得状态仍然是 \(奇 \ 奇 \ 偶\) 的情况,但是此时 \(奇\ 奇\) 中还存在这样的一对数使得 \(A\) 最后面临 \(偶 \ 偶 \ 偶\) 的必败态
- 时间复杂度:\(O(n)\)
代码
// Problem: A Game of Taking Numbers
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/43898/G
// Memory Limit: 524288 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int n,t;
int main()
{
for(scanf("%d",&t);t;t--)
{
scanf("%d",&n);
vector<int> a[3];
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
a[x%3].pb(x);
}
if(n&1)
{
puts("His little girlfriend");
continue;
}
if(a[0].size()%2==0&&a[1].size()%2==0&&a[2].size()%2==0)
{
puts("rqd");
continue;
}
bool f=false;
for(int i=0;i<2;i++)
if(a[i].size()%2==0)swap(a[i],a[2]);
unordered_map<int,int> mp[3];
for(int i:a[1])mp[1][i]++;
for(int i:a[0])
{
mp[0][i]++;
for(int j=-3;j<=3;j++)
if(mp[1].count(i+j))
{
f=true;
break;
}
}
bool fl[2]={0};
for(int i=0;i<a[2].size();i++)
{
bool is[2]={0};
for(int j=-3;j<=3;j++)
{
if(mp[0].count(a[2][i]+j))is[0]=true;
if(mp[1].count(a[2][i]+j))is[1]=true;
}
if(!is[0]&&!is[1])continue;
f|=(is[0]&&fl[1]||is[1]&&fl[0]);
if(f)break;
if(is[0]&&!is[1])fl[0]=true;
else if(is[1]&&!is[0])fl[1]=true;
else
{
bool t=false;
for(int k=0;k<a[2].size();k++)
{
if(k==i)continue;
for(int j=-3;j<=3;j++)
{
if(mp[0].count(a[2][k]+j))t=true;
if(mp[1].count(a[2][k]+j))t=true;
}
}
if(t)f=true;
break;
}
}
puts(f?"rqd":"His little girlfriend");
}
return 0;
}