题目大意:有n个地方需要通讯,每个地方可以用两种基站里面的其中一种,有p种基站, 每种基站能够接收的频率在l到r范围内,最大的频率是M,有m对基站会相互干扰,所以不能同时选。
要求一种频率,使得每个地方都能通讯。 输出使用的频率和哪几个基站。
题目分析: 这题明显可以用2sat来做,但是我们知道只有在确定频率的基础上我们才可以建图。 所以最朴素的做法就是枚举各种频率,然后每次建图判断,这样做的时间复杂度比较高。
这题有一个巧妙的方法,我们可以把频率也看成一个点,这样就不用重复建图了。 比如第i个点的两个对立面,频率大于等于i和频率小于i。 那么我们可以这么建图:
频率小于i可以推出频率小于i+1
频率大于等于i+1,可以推出频率大于等于i
某个基站的频率范围是l到r,那么我们可以建边。
频率小于l推出这个基站不选
这个基站选了可以推出频率大于等于l
频率大于等于r+1可以推出这个基站不选
这个基站选了可以推出频率小于r+1。
再加上本身2sat的建图,边的数量大概在400万左右,可以承受。
最后找答案,也就是频率,也有一定的技巧。
我们需要先计算出哪些小于i的频率选了。
因为我们知道频率小于i,可以推出频率小于i+1,也可以推出频率小于i+2,。。。。 所以肯定有一段连续的频率小于某些值的被选了。
所以我们可以倒着枚举,因为频率小于M+1是一定选了的。
时间复杂度是线性的。

1 #include<bits/stdc++.h>
2 using namespace std;
3 int const N=400000+10;
4 int const M=1600000+10;
5 int h[M],cnt,bl[M],dfn[M],low[M],sum,n,p,t,s[M],top,vis[M],m;
6 struct edge{
7 int to,nt;
8 }e[M<<2];
9 void add(int a,int b){
10 e[++cnt].to=b;
11 e[cnt].nt=h[a];
12 h[a]=cnt;
13 }
14 void dfs(int x){
15 dfn[x]=low[x]=++cnt;
16 s[++top]=x;
17 for(int i=h[x];i;i=e[i].nt){
18 int v=e[i].to;
19 if(!dfn[v]){
20 dfs(v);
21 low[x]=min(low[x],low[v]);
22 }else if(!bl[v]) low[x]=min(low[x],dfn[v]);
23 }
24 if(dfn[x]==low[x]){
25 sum++;
26 while (s[top]!=x){
27 bl[s[top]]=sum;
28 top--;
29 }
30 bl[x]=sum; top--;
31 }
32 }
33 int main(){
34 scanf("%d%d%d%d",&n,&p,&m,&t);
35 for(int i=1;i<=n;i++){
36 int x,y;
37 scanf("%d%d",&x,&y);
38 add(x<<1,y<<1|1);
39 add(y<<1,x<<1|1);
40 }
41 for(int i=1;i<=m;i++){
42 add((p+i)<<1|1,(p+i+1)<<1|1);
43 add((p+i+1)<<1,(p+i)<<1);
44 }
45 for(int i=1;i<=p;i++){
46 int x,y;
47 scanf("%d%d",&x,&y);
48 add((p+x)<<1|1,i<<1);
49 add(i<<1|1,(p+x)<<1);
50 add((p+y+1)<<1,i<<1);
51 add(i<<1|1,(p+y+1)<<1|1);
52 }
53 for(int i=1;i<=t;i++){
54 int x,y;
55 scanf("%d%d",&x,&y);
56 add(x<<1|1,y<<1);
57 add(y<<1|1,x<<1);
58 }
59 cnt=0; int ans=0;
60 for(int i=2;i<=(p+m)*2+3;i++)
61 if(!bl[i]) dfs(i);
62 int f=m;
63 for(int i=1;i<=p+m+1;i++)
64 if(bl[i<<1]==bl[i<<1|1]) {
65 printf("-1\n");
66 return 0;
67 }else{
68 vis[i]=bl[i<<1|1]<bl[i<<1];
69 if(i<=p) ans+=vis[i];
70 }
71 while (f>0 && vis[p+f+1]) f--;
72 printf("%d %d\n",ans,f+1);
73 for(int i=1;i<=p;i++)
74 if(vis[i]) printf("%d ",i);
75 return 0;
76 }
