Company Organization (AOJ1332)

問題:
1~Nの番号がついた集合がある.集合間の制約が順にM個来るので,どこで最初に矛盾するか答えてください.
制約は5種類で,
1:A⊆B 2:A=B 3:A≠B 4:A∩B=∅ 5:A∩B≠∅
タイプとAとBの番号が制約として与えられる.
N≤100,M≤10000


頭1,実装1,ライブラリ1
簡単だけど注意力がいる







まずにぶたんすればはじめk個の制約で矛盾するかわかればよい.
まず1,2を処理して,包含関係によるposetをつくる(処理した後に推移閉包を取る)
基本的にA∩B=∅ かつ a⊆A かつ b⊆B かつ a∩b≠∅ みたいなペアがなければ矛盾しないから,推移閉包を取った後に4の情報を下に伝播させていく(O(N^4)) でそのあと3,5で矛盾するかどうか確かめる みたいな流れなんだけど,A=∅みたいな形が出てくるのでこれだけでは済まない.例えば,a⊆A b⊆B a∩A=∅ b∩B=∅ みたいなケースだと,a=∅かつb=∅がわかるのでa≠bとは矛盾してしまう.なので∅かどうか みたいなのも持たなければいけない.どういう時にX=∅になるかというと,X⊆A X⊆B A∩B=∅ のとき.これを4を処理するときにO(N)かけてやっておけば全体O(N^4 * log M)で解ける.

頑張ればもうちょっとオーダーよくなりそうだけどどうだろう

#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)(n);i++)
#define rep1(i,n) for(int i=1;i<=(int)(n);i++)
#define all(c) c.begin(),c.end()
#define pb push_back
#define fs first
#define sc second
#define show(x) cout << #x << " = " << x << endl
#define chmin(x,y) x=min(x,y)
#define chmax(x,y) x=max(x,y)
using namespace std;
int N,M,t[10000],v[10000],u[10000];
bool can(int M){
	bool e[100][100]={},emp[10000]={},no[100][100]={};
	rep(i,N) e[i][i]=1;
	rep(i,M){
		if(t[i]==1) e[u[i]][v[i]]=1;
		if(t[i]==2) e[u[i]][v[i]]=e[v[i]][u[i]]=1;
	}
	rep(i,N) rep(j,N) rep(k,N) e[j][k]|=(e[j][i]&&e[i][k]);
	rep(i,M){
		if(t[i]==4){
			int a=v[i],b=u[i];
			rep(c,N) if(e[a][c]&&e[b][c]) emp[c]=1;
			no[a][b]=no[b][a]=1;
		}
	}
	rep(i,N) if(emp[i]){
		rep(j,N) if(e[i][j]) emp[j]=1;
	}
	rep(i,N) rep(j,N) if(no[i][j]){
		rep(I,N) rep(J,N){
			if(e[i][I]&&e[j][J]){
				no[I][J]=1;
			}
		}
	}
	// puts("no");
	// rep(i,N){
	// 	rep(j,N) cout<<no[i][j]<<" ";
	// 	puts("");
	// }
	rep(i,M){
		int a=v[i],b=u[i];
		if(t[i]==3){
			if(emp[a]&&emp[b]) return 0;
			if(e[a][b]&&e[b][a]) return 0;
		}
		if(t[i]==5){
			if(emp[a]||emp[b]) return 0;
			if(no[a][b]) return 0;
		}
	}
	return 1;
}
int main(){
	while(true){
		cin>>N>>M;
		if(N==0) break;
		rep(i,M){
			cin>>t[i]>>v[i]>>u[i],v[i]--,u[i]--;
		}
		int ub=M+1,lb=0;
		while(ub-lb>1){
			int m=(ub+lb)/2;
			if(can(m)) lb=m;
			else ub=m;
		}
		cout<<lb<<endl;
	}
}