Luogu4700 CEOI2011 Traffic Tarjan、搜索

传送门

题意:给出平面上$N$个点,它们一定在左下角为$(0,0)$,右上角为$(A,B)$的一个矩形内的整点上(包括边界),而且会给出$M$条呈直线的边,其中有有向边也有无向边,保证任意两条边不会在非顶点处相交。现在我们定义横坐标为$0$的点为在西边的点,横坐标为$A$的点为在东边的点,现在请求出每一个在西边的点都能通过给出的边到达多少在东边的点。$N \leq 3 \times 10^5 , M \leq 9 \times 10^5 , 1 \leq A , B , \text{点的坐标} \leq 10^9$


其实还是挺考验思维的

首先一看到这道题:诶这不是裸的$tarjan$套记搜吗,然后打完发现会算重很多东西,样例都过不了qwq。

其实题目中有一句话十分隐蔽:保证任意两条边不会在非顶点处相交,这会给我们什么信息呢?

直接讲结论:除了无法到达的点,每一个西边的能够到达的东边的点都会对应纵坐标的一段区间,也就是说一定是从上往下一段连续的点,而如果其中有无法到达的点,那么一定是所有的西边的点都无法到达它。这个结论与引水入城的思想十分类似。所以我们可以先缩点,然后在剩下的$DAG$上进行记忆化搜索,把每一个西边的点能够到达的东边的点对应的区间记录下来(显然这个是很好记录的,只要记录两个端点即可),然后再去检索是否有无法到达且在这一段区间内的东边的点,将其扣除即可。

关于下面的代码,思想与上面一致,只是为了易于统计答案,将所有的边都取反,然后统计每一个东边的点能够影响的西边的点的区间,然后用前缀和获得每一个西边节点的答案,最后去看每一个西边的点对应的强连通分量是否被到达过(没有就表示没有东边的点可以到达它,自然答案就是$0$)

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 inline int read(){
  5     int a = 0;
  6     bool f = 0;
  7     char c = getchar();
  8     while(c != EOF && !isdigit(c)){
  9         if(c == '-')
 10             f = 1;
 11         c = getchar();
 12     }
 13     while(c != EOF && isdigit(c)){
 14         a = (a << 3) + (a << 1) + (c ^ '0');
 15         c = getchar();
 16     }
 17     return f ? -a : a;
 18 }
 19 
 20 const int MAXN = 300010;
 21 struct Edge{
 22     int end , upEd;
 23 }Ed[MAXN * 6] , newEd[MAXN * 6];
 24 struct node{
 25     int y , ind;
 26 }now[MAXN];
 27 int head[MAXN] , newHead[MAXN] , in[MAXN] , len[2][MAXN] , ind[MAXN] , num[MAXN] , s[MAXN] , dfn[MAXN] , low[MAXN];
 28 int N , M , A , B , cntEd , cntNode , ts , headS , cntNewEd , cntSCC;
 29 bool vis[MAXN] , ins[MAXN] , ine[MAXN];
 30 queue < int > q;
 31 
 32 inline void addEd(Edge* Ed , int* head , int& cntEd , int a , int b){
 33     Ed[++cntEd].end = b;
 34     Ed[cntEd].upEd = head[a];
 35     head[a] = cntEd;
 36 }
 37 
 38 void tarjan(int x){
 39     dfn[x] = low[x] = ++ts;
 40     vis[x] = ins[x] = 1;
 41     s[++headS] = x;
 42     for(int i = head[x] ; i ; i = Ed[i].upEd){
 43         if(!vis[Ed[i].end])
 44             tarjan(Ed[i].end);
 45         else
 46             if(!ins[Ed[i].end])
 47                 continue;
 48         low[x] = min(low[Ed[i].end] , low[x]);
 49     }
 50     if(dfn[x] == low[x]){
 51         cntSCC++;
 52         do{
 53             int t = s[headS];
 54             in[t] = cntSCC;
 55             ins[t] = 0;
 56             if(ind[t]){
 57                 len[0][cntSCC] = min(len[0][cntSCC] , ind[t]);
 58                 len[1][cntSCC] = max(len[1][cntSCC] , ind[t]);
 59             }
 60         }while(s[headS--] != x);
 61     }
 62 }
 63 
 64 void create(){
 65     for(int i = 1 ; i <= N ; i++)
 66         for(int j = head[i] ; j ; j = Ed[j].upEd)
 67             if(in[Ed[j].end] != in[i])
 68                 addEd(newEd , newHead , cntNewEd , in[i] , in[Ed[j].end]);
 69 }
 70 
 71 void dfs(int x){
 72     if(vis[x])
 73         return;
 74     vis[x] = 1;
 75     for(int i = newHead[x] ; i ; i = newEd[i].upEd){
 76         dfs(newEd[i].end);
 77         len[0][x] = min(len[0][x] , len[0][newEd[i].end]);
 78         len[1][x] = max(len[1][x] , len[1][newEd[i].end]);
 79     }
 80 }
 81 
 82 bool operator <(node a , node b){
 83     return a.y > b.y;
 84 }
 85 
 86 int main(){
 87 #ifdef LG
 88     freopen("4700.in" , "r" , stdin);
 89     freopen("4700.out" , "w" , stdout);
 90 #endif
 91     memset(len[0] , 0x3f , sizeof(len[0]));
 92     N = read();
 93     M = read();
 94     A = read();
 95     B = read();
 96     for(int i = 1 ; i <= N ; i++){
 97         int x = read() , y = read();
 98         if(x == A)
 99             ine[i] = 1;
100         else
101             if(!x){
102                 now[++cntNode].y = y;
103                 now[cntNode].ind = i;
104             }
105     }
106     for(int i = 1 ; i <= M ; i++){
107         int a = read() , b = read() , c = read();
108         addEd(Ed , head , cntEd , b , a);
109         if(c == 2)
110             addEd(Ed , head , cntEd , a , b);
111     }
112     sort(now + 1 , now + cntNode + 1);
113     for(int i = 1 ; i <= cntNode ; i++)
114         ind[now[i].ind] = i;
115     for(int i = 1 ; i <= N ; i++)
116         if(!vis[i])
117             tarjan(i);
118     memset(vis , 0 , sizeof(vis));
119     create();
120     for(int i = 1 ; i <= N ; i++)
121         if(ine[i]){
122             dfs(in[i]);
123             if(len[0][in[i]] <= len[1][in[i]]){
124                 num[len[0][in[i]]]++;
125                 num[len[1][in[i]] + 1]--;
126             }
127         }
128     for(int i = 1 ; i <= cntNode ; i++)
129         num[i] += num[i - 1];
130     for(int i = 1 ; i <= cntNode ; i++)
131         cout << (!vis[in[now[i].ind]] ? 0 : num[i]) << endl;
132     return 0;
133 }

转载于:https://www.cnblogs.com/Itst/p/9852673.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值