$("head").append('')

P4355 [CERC2015] Kernel Knights

P4355 [CERC2015] Kernel Knights

传送门

题目描述

马上长矛比武是一种中世纪的比赛,参赛者骑在马上,用木制长矛高速冲刺,试图击中对方。共有 2n 名骑士参加了马上长矛比武比赛——来自两个伟大敌对家族的 n 名骑士。到达后,每位骑士向另一家族的一名骑士发起了挑战。

一个“核”被定义为骑士的某个子集 S,具有以下两个特性:

  • S 中没有骑士被 S 中的其他骑士挑战。
  • 不在 S 中的每个骑士都被 S 中的某个骑士挑战。

给定发出的挑战集,找到一个“核”。保证“核”总是存在。

输入格式

第一行包含一个整数 n (1 ≤ n ≤ 100000)——每个家族的骑士数量。第一个家族的骑士用整数 1 到 n 表示,第二个家族的骑士用整数 n+1 到 2n 表示。

接下来的行包含整数 \(f_1\), \(f_2\),..., \(f_n\)——第 k 个整数 \(f_k\) 是骑士 k 挑战的骑士的编号 \((n+1 ≤ f_k ≤ 2n)\)

接下来的行包含整数 \(s_1\),\(s_2\),...,\(s_n\)——第 k 个整数 \(s_k\) 是骑士 n+k 挑战的骑士的编号 \((1 ≤ s_k ≤ n)\)

输出格式

在一行中输出“核”中的骑士编号。如果有多个解,你可以输出其中任何一个。

SPJ 的格式校验较为严格,请在每个数字后面都输出一个空格,且在最后一个空格输出后请不要输出任何符号。

输入输出样例 #1

输入 #1

4 
5 6 7 7 
1 3 2 3

输出 #1

1 2 4 8

说明/提示

Central Europe Regional Contest 2015 Problem K。

题面翻译由 ChatGPT-4o 提供。


题意:

寻找集合 \(S\) ,使得:

  1. \(S\) 中的骑士不能互相挑战。
  2. 不在 \(S\) 中的骑士都会被 \(S\) 中的骑士挑战。

思路:

首先看一下挑战的具体规则:

在第 \(i\) 个位置上的数 \(k_i\) 代表着编号为 \(i\) 的骑士挑战编号为 \(k_i\) 的骑士。

观察第二条要求,不难发现,在 \(S\) 外的骑士一定都被挑战了,由此我们可以知道,只要一个骑士 \(a\) 并没有被挑战过,那么他一定在集合 \(S\)
同时,因为集合内的骑士不能互相攻击,所以被骑士 \(a\) 挑战的那名骑士 \(b\) 一定在集合 \(S\) 的外面
最后,队伍中可能出现一个非常招仇恨的骑士,他会被挑战很多次,这个时候我们只用把他排出集合 \(S\) 一次就好了(要是祂被打一次赶祂一次岂不是给人家发配边疆了)。

这时候,可能有严谨的小伙伴要问了:

那集合外面的骑士的攻击怎么办呢?

事实上,集合外的攻击完全不用管,我们只需要看好集合内的骑士让他们不要打架就可以了,至于那些被流放到集合外的骑士就不属于我们的管理范围了~。


实现:

由于要进行踢出集合和加入集合两种操作,不难想到,我们可以开一个桶标记被攻击次数,并使用队列维护此集合:

  1. 先把没有被攻击过的骑士加入队列。
  2. 进行完第一步后,把队列外没有被队列内骑士攻击的都加入队列。

然后就可以愉快的通过啦!(估计不会有人和我一样傻傻忘记输出空格全似了吧)。

AC Code:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,a[N],b[N],c[N];
queue<int>q;
int main(){
    while(cin>>n){
        memset(b,0,sizeof(b));//多测不清空 
        memset(c,0,sizeof(c));//亲人两行泪 
        int m=n*2;
        for(int i=1;i<=m;i++){
            cin>>a[i]; 
            b[a[i]]++;//开桶标记被攻击次数 
        }
        for(int i=1;i<=m;i++){
        	if(!b[i]){
        		q.push(i);//先处理没被打过的,一定在集合里 
        	}
        }
        while(!q.empty()){//处理操作 2 
            int p=q.front();
            q.pop();
            c[p]=1;
            if(c[a[p]])continue;//不要发配边疆,只踢出一次,方便又快捷 
            c[a[p]]=-1;b[a[a[p]]]--;//集合外面的骑士自己一边玩去 
            if(b[a[a[p]]]==0)q.push(a[a[p]]);//没人打的骑士也进来 
        }
        for(int i=1;i<=m;i++){
        	if(i<=n&&c[i]>=0||i>n&&c[i]==1)cout<<i<<" ";
        }
        //注意输出格式 qwq 
        cout<<'\n';
    }
    return 0;//华丽结束
}

说句闲话,研究骑士的最好方式是……

posted @ 2025-10-12 15:17  lain_yc  阅读(19)  评论(0)    收藏  举报
$("head").append('')