一、题目
二、解法
( t 2sat) 的做法就不讲了,线段树优化建图要写麻
从另一个角度切入,我们可以先枚举每个小组中的学生人数,可以知道老师是否能分配到这个小组中,然后根据 (m) 个限制来对老师二分图染色即可。
瓶颈在于枚举学生人数,先不考虑总人数的限制,发现最优的取值是 (n_1=min{r_i},n_2=max{l_i})
- 如果 (n_1geq 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
",n1,n2);
for(int i=1;i<=n;i++)
printf("%d",c[i]);
}