CF538H Summer Dichotomy

一、题目

点此看题

二、解法

\(\tt 2sat\) 的做法就不讲了,线段树优化建图要写麻

从另一个角度切入,我们可以先枚举每个小组中的学生人数,可以知道老师是否能分配到这个小组中,然后根据 \(m\) 个限制来对老师二分图染色即可。

瓶颈在于枚举学生人数,先不考虑总人数的限制,发现最优的取值是 \(n_1=\min\{r_i\},n_2=\max\{l_i\}\)

  • 如果 \(n_1\geq n_2\),那么所有区间两两相交,这两个端点值最优。
  • 如果 \(n_1<n_2\),那么增大 \(n_1\) 会让某个老师无法选组,减少 \(n_2\) 也会让某个老师无法选组,而减少 \(n_1\) 只会让 \(1\) 组的覆盖范围更少,增大 \(n_2\) 同理,所以它们是最优的。

如果 \(n_1+n_2\) 不符合总人数的限制怎么办?综合上面的讨论我们可以发现如果 \(n_1+n_2<t\) 那么我们增大 \(n_2\),如果 \(n_1+n_2>t\) 那么我们减小 \(n_1\),这样调整也是最优的。

最后只需要验证这一组 \((n_1,n_2)\) 是否能跑出二分图匹配即可,时间复杂度 \(O(n)\)

三、总结

区间问题着重考虑端点,可以将需要枚举的东西用贪心来最优化。

#include <cstdio>
#include <vector>
#include <cstdlib>
using namespace std;
const int M = 100005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,a,b,n1,n2,l[M],r[M],c[M],vis[M];
vector<int> g[M];
void dfs(int u)
{
	vis[u]=1;
	for(auto v:g[u])
	{
		if(c[v]==c[u])
		{
			puts("IMPOSSIBLE");
			exit(0);
		}
		c[v]=3-c[u];
		if(!vis[v]) dfs(v);
	}
}
signed main()
{
	a=read();b=read();n=read();m=read();
	n1=1e9;n2=0;
	for(int i=1;i<=n;i++)
	{
		l[i]=read();r[i]=read();
		n1=min(n1,r[i]);
		n2=max(n2,l[i]);
	}
	if(n1+n2<a) n2=a-n1;
	if(n1+n2>b) n1=b-n2;
	if(n1<0 || n2<0)
	{
		puts("IMPOSSIBLE");
		return 0;
	}
	for(int i=1;i<=n;i++)
	{
		int f1=l[i]<=n1 && n1<=r[i];
		int f2=l[i]<=n2 && n2<=r[i];
		if(!f1 && !f2)
		{
			puts("IMPOSSIBLE");
			return 0;
		}
		if(!f1) c[i]=2;
		if(!f2) c[i]=1;
	}
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i=1;i<=n;i++)
		if(c[i] && !vis[i]) dfs(i);
	for(int i=1;i<=n;i++)
		if(!vis[i]) c[i]=1,dfs(i);
	puts("POSSIBLE");
	printf("%d %d\n",n1,n2);
	for(int i=1;i<=n;i++)
		printf("%d",c[i]);
}
posted @ 2021-09-03 14:46  C202044zxy  阅读(63)  评论(0编辑  收藏  举报