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;
}
posted @ 2022-10-23 18:11  zyy2001  阅读(752)  评论(0编辑  收藏  举报