[cf1215F]Radio Stations

这道题如果没有功率的限制,显然就是一个裸的2-sat

考虑将功率的限制也放在图上:如果选择了功率i,那么功率区间不包含它的点只能不选,连边即可

但是这样建图的边数是o(n^2),需要优化

将功率区间分为两种,一种在这个点前面,另一种在这个点的后面

同样将功率也裂成两个点,分别连向这两种区间,因为功率i的第1种点一定是功率i+1的第一种点的子集,因此(i+1)1->i1和功率右区间恰好在i+1的点,同理(i-1)2->i2和功率左区间恰好为i-1的点

对图求强连通,如果裂成的两个点中在同一个强连通就不行,否则所有选1的区间都要选,功率是最大的i2

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

 

转载于:https://www.cnblogs.com/PYWBKTDA/p/11538332.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值