题意:给出平面上$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 }