[loj3524]钥匙

由于到达关系具有传递性,可以考虑不断将若干个可以相互到达的点缩点,并且当两个点只能单向到达时,能到达另一个点的点一定不是最小值

由此,我们来考虑dfs,即不断从一个节点开始,遍历其可以到达的点,当发现了环即将这些点合并(启发式合并),当发现了已经完全搜过(不在栈中)的点即一定不能作为最小值

在实现上,有一些困难,注意以下细节:

1.对每一个点还要维护一个钥匙集合,也参与启发式合并

2.启发式合并时可能有新的边可以到达,在加入时判定即可

3.由于要弹出元素,且不像强连通分量一样保证必然弹出,因此建议手动模拟栈来实现

4.在搜过一条边后将其弹出边集,以避免合并后被其他点重复搜索

5.判定是否遍历到只能单向到达的点,可以在搜索结束后再判定

  1 #include<bits/stdc++.h>
  2 #include"keys.h"
  3 using namespace std;
  4 #define N 300005
  5 #define vi vector<int>
  6 #define fi first
  7 #define se second
  8 stack<int>st;
  9 set<int>key[N];
 10 set<int>::iterator it1;
 11 vector<int>ans,G0[N];
 12 set<pair<int,int> >G1[N];
 13 set<pair<int,int> >::iterator it2;
 14 int n,m,mn,dfn[N],vis[N],f[N],sz[N];
 15 int find(int k){
 16     if (k==f[k])return k;
 17     return f[k]=find(f[k]);
 18 }
 19 int get_sum(int k){
 20     return key[k].size()+G0[k].size()+G1[k].size();
 21 }
 22 void add(int x,int y,int z){
 23     x=find(x);
 24     if (key[x].find(z)!=key[x].end())G0[x].push_back(y);
 25     else G1[x].insert(make_pair(z,y));
 26 }
 27 void merge(int x,int y){
 28     x=find(x),y=find(y);
 29     if (x==y)return;
 30     if (get_sum(x)<get_sum(y))swap(x,y);
 31     f[y]=x;
 32     sz[x]+=sz[y];
 33     for(it1=key[y].begin();it1!=key[y].end();it1++){
 34         int k=(*it1);
 35         key[x].insert(k);
 36         while (1){
 37             it2=G1[x].lower_bound(make_pair(k,0));
 38             if ((it2==G1[x].end())||((*it2).fi!=k))break;
 39             G0[x].push_back((*it2).se);
 40             G1[x].erase(it2);
 41         }
 42     }
 43     key[y].clear();
 44     for(int i=0;i<G0[y].size();i++)G0[x].push_back(G0[y][i]);
 45     G0[y].clear();
 46     for(it2=G1[y].begin();it2!=G1[y].end();it2++)add(x,(*it2).se,(*it2).fi);
 47     G1[y].clear();
 48 }
 49 void dfs(int k){
 50     st.push(k);
 51     while (!st.empty()){
 52         k=st.top();
 53         dfn[k]=1;
 54         if (G0[k].empty()){
 55             vis[k]=1;
 56             st.pop();
 57             continue;
 58         }
 59         int x=find(G0[k].back());
 60         G0[k].pop_back();
 61         if (!dfn[x]){
 62             st.push(x);
 63             continue;
 64         }
 65         if (vis[x])continue;
 66         while (st.top()!=x){
 67             merge(st.top(),k);
 68             st.pop();
 69         }
 70         merge(st.top(),k);
 71         st.pop();
 72         st.push(find(k));
 73     }
 74 }
 75 void check(int x,int y,int z){
 76     x=find(x),y=find(y);
 77     if (x!=y){
 78         if (key[x].find(z)!=key[x].end())vis[x]=1;
 79         if (key[y].find(z)!=key[y].end())vis[y]=1;
 80     }
 81 }
 82 vi find_reachable(vi r,vi u,vi v,vi c){
 83     n=r.size(),m=u.size();
 84     for(int i=1;i<=n;i++){
 85         f[i]=i,sz[i]=1;
 86         key[i].insert(r[i-1]);
 87     }
 88     for(int i=0;i<m;i++){
 89         u[i]++,v[i]++;
 90         add(u[i],v[i],c[i]);
 91         add(v[i],u[i],c[i]);
 92     }
 93     for(int i=1;i<=n;i++)
 94         if (!dfn[i])dfs(i);
 95     memset(vis,0,sizeof(vis));
 96     for(int i=0;i<m;i++)check(u[i],v[i],c[i]);
 97     mn=0x3f3f3f3f;
 98     for(int i=1;i<=n;i++)
 99         if (!vis[find(i)])mn=min(mn,sz[find(i)]);
100     for(int i=1;i<=n;i++)
101         if ((!vis[find(i)])&&(sz[find(i)]==mn))ans.push_back(1);
102         else ans.push_back(0);
103     return ans;
104 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/15006186.html