Official

B - Perfect Editorial by mechanicalpenciI


誰がどの問題を解いたかの情報を管理することを考えます。

具体的には、\(1\leq i\leq N\), \(1\leq j\leq M\) をみたす整数の組 \((i,j)\) について、人 \(i\) が問題 \(j\) を解いたかどうかを管理するフラグの配列を用意します。最初、すべてのフラグは False です。
また、それぞれの人について、その人が全ての問題を解いたことを記録したかのフラグも用意します。こちらのフラグも最初すべて False です。
最後に、実際に出力する、全ての問題を解いた人を解いたタイミングが早い順番に並べた順に並べるためのリスト (vector) を用意します。

\(K\) 個のイベントそれぞれについて順番に、次のことを繰り返します。

  • \(A_i\) が問題 \(B_j\) を解いたフラグを True に変更します。
  • それぞれの人について、その人がすべての問題を解いたか確認します。人 \(i\) がすべての問題を解いており、そのことを記録したかのフラグが False のとき、出力用リストに人 \(i\) を追加し、人 \(i\) について記録したかのフラグを True に変更します。

最後に、リストに記録された人の番号を空白区切りで出力すれば良いです。

なお、毎度すべてのフラグを確認する上記の手順の計算量は \(O(KNM)\) となりますが、同じイベントは \(2\) 回以上起きないことから、\(K\leq NM\) であり、計算量は特に \(O(N^2M^2)\) となります。今回の問題の制約下では十分に高速です。

実際には、次のように高速化することができます。

  • \(i\) 番目のイベントのタイミングで新しくすべての問題を解く可能性があるのは人 \(A_i\) のみであるので、フラグを確認するのは人 \(A_i\) のみについてでよい。このとき、計算量は \(O(KM)\) となる。
  • 同じイベントが \(2\) 回以上起きないことから、それぞれの人がどの問題を解いたかを管理する必要はなく、解いた問題数だけ管理すれば良い。このとき、計算量は \(O(K)\) となる。(空間計算量は愚直に行った場合 \(O(N)\) となる。)

c++ による実装例(愚直な解法):

#include <bits/stdc++.h>

using namespace std;

int main(void){
	int n,m,k;
	int a,b,cnt;
	bool solved[10][10]={};
	bool recorded[10]={};
	vector<int>ans;

	cin>>n>>m>>k;
	for(int ievent=0;ievent<k;ievent++){
		cin>>a>>b;
		solved[a-1][b-1]=true;
		for(int i=0;i<n;i++){
			cnt=0;
			for(int j=0;j<m;j++){
				if(solved[i][j])cnt++;
			}
			if((cnt==m)&&(!recorded[i])){
				ans.push_back(i+1);
				recorded[i]=true;
			}
		}
	}

	int sz=ans.size();
	for(int i=0;i<sz;i++){
		cout<<ans[i];
		if(i<(sz-1))cout<<" ";
		else cout<<endl;
	}
	return 0;
}

c++ による実装例(\(O(K)\) 解法):

#include <bits/stdc++.h>
using namespace std;

int main(void){
	int n,m,k;
	int a,b;
	int cnt[10]={};
	vector<int>ans;

	cin>>n>>m>>k;
	for(int i=0;i<k;i++){
		cin>>a>>b;
		cnt[a-1]++;
		if(cnt[a-1]==m)ans.push_back(a);
	}

	int sz=ans.size();
	for(int i=0;i<sz;i++){
		cout<<ans[i];
		if(i<(sz-1))cout<<" ";
		else cout<<endl;
	}
	return 0;
}

Python による実装例(愚直な解法):

n,m,k=map(int, input().split())

solved=[[False for j in range(m)]for i in range(n)]
recorded=[False for i in range(n)]
ans=[]

for ievent in range(k):
    a,b=map(int, input().split())
    solved[a-1][b-1]=True
    for i in range(n):
        cnt=0
        for j in range(m):
            if solved[i][j]:
                cnt+=1
        if cnt==m and recorded[i]==False:
            ans.append(i+1)
            recorded[i]=True

print(*ans)

posted:
last update: