洛谷 P6575 - [BalticOI 2017] Friends(暴力)

首先先把不符合条件一的情况判掉。这种情况很青蛙。

定义一个集合 \(S\) 合法,当且仅当 \(|S|\le p\)\(S\) 中与外界相连的边数 \(\le q\)

那么可以注意到一件事:如果 \(S,T\) 合法,那么 \(S\text{\\}T,T\text{\\}S\) 之一合法(我也不知道为什么要从这个方向考虑,但是确实很玄妙)

证明:由于 \(|S|,|T|\le p\),所以 \(|S\text{\\}T|,|T\text{\\}S|\le p\)

\(f(S)\) 表示 \(S\) 连出去的边数。考虑比较 \(f(S)+f(T)\)\(f(S\text{\\}T)+f(T\text{\\}S)\)。记 \(V=S\cap T,W=U\text{\\}(S\cup T)\),发现 \(V\)\(W\) 之间的边在前者中算了两次,在后者中算了零次,而其他的边在两式中计算次数是相同的。故 \(f(S)+f(T)\ge f(S\text{\\}T)+f(T\text{\\}S)\),而 \(f(S),f(T)\le q\),故 \(f(S\text{\\}T)+f(T\text{\\}S)\le 2q\),得证。

这样,我们只用对每个点找到一个包含其的合法的集合,然后按照上面的步骤消除重复元素即可。

考虑怎样对每个点找到包含其的集合。我们维护一个集合 \(S\),考察每个点时先将其放进去,然后考察相邻的点,我们暴力搜索将它放入集合和不放入集合两种情况,不难发现,如果放进去,那么 \(|S|\) 加一,如果不放进去,那么已经确定的 \(S\) 与外界点所连的边数加一,因此最多决策 \(p+q\) 轮,这样单次复杂度就是 \(2^{p+q}·(p+q)\)

总复杂度大概是 \(n2^{p+q}(p+q)\),很卡常,需要加不少剪枝才能过。

const int MAXN=2500;
int n,p,q,vis[MAXN+5][MAXN+5],has[MAXN+5];
vector<int>g[MAXN+5];set<int>st[MAXN+5];
bool check(set<int>s){
	if(s.size()>p)return 0;int c=0;
	for(int x:s)for(int y:g[x]){
		c+=(s.find(y)==s.end());
		if(c>q)return 0;
	}return 1;
}
bool dfs(int x,set<int>in,set<int>nei,set<int>out){
	in.insert(x);
	int sum=0;for(int x:in)for(int y:g[x])if(out.find(y)!=out.end()){
		sum++;if(sum>q)break;
	}
	if(in.size()>p||sum>q)return 0;
	if(check(in)){st[x]=in;for(int x:in)has[x]=1;return 1;}
	for(int y:g[x])if(in.find(y)==in.end()&&out.find(y)==out.end()&&y!=x)nei.insert(y);
	while(!nei.empty()){
		int y=*nei.begin();nei.erase(nei.find(y));
		if(dfs(y,in,nei,out))return 1;out.insert(y);
	}return 0;
}
int main(){
	scanf("%d%d%d",&n,&p,&q);
	for(int i=0;i<n;i++){
		int len;scanf("%d",&len);
		while(len--){int x;scanf("%d",&x);vis[i][x]=1;g[i].pb(x);}
	}
	for(int i=0;i<n;i++)for(int j=0;j<i;j++)if(vis[i][j]+vis[j][i]==1)
		return puts("detention"),0;
	for(int i=0;i<n;i++)if(!has[i]){
		set<int>in,nei,out;dfs(i,in,nei,out);
		if(!has[i])return puts("detention"),0;
	}puts("home");
//	for(int i=0;i<n;i++){printf("%d: ",i);for(int x:st[i])printf("%d ",x);printf("\n");}
	for(int i=0;i<n;i++)for(int j=0;j<i;j++){
		set<int>s1=st[i],s2=st[j];
		for(int x:st[i])if(st[j].find(x)!=st[j].end())s1.erase(s1.find(x));
		for(int x:st[j])if(st[i].find(x)!=st[i].end())s2.erase(s2.find(x));
		if(check(s1))st[i]=s1;else st[j]=s2;
	}
	vector<vector<int> >res;
	for(int i=0;i<n;i++)if(!st[i].empty()){
		vector<int>vec;for(int x:st[i])vec.pb(x);
		res.pb(vec);
	}printf("%d\n",res.size());
	for(auto vec:res){
		printf("%d",vec.size());
		for(int x:vec)printf(" %d",x);
		printf("\n");
	}
	return 0;
}
/*
15 2 3
3 1 2 3
2 0 2
3 0 1 3
3 0 2 4
4 3 5 6 8
1 4
2 4 7
3 6 8 9
3 4 7 9
2 7 8
2 11 13
3 10 12 13
2 11 13
3 10 11 12
0
*/
posted @ 2022-12-22 12:31  tzc_wk  阅读(67)  评论(0编辑  收藏  举报