• JZOJ 5305 C先生


    题意:

    有一个n个点,m条边的图,没有重边、自环,且每一条边最多属于一个环路。

    给出q组询问,每次询问u,v两点间的路径有多少种可能。

    思路:

    先看下方样例说明:

    由样例说明可以得知,路径上每经过一个环,路径种数就会乘2,而且最终答案一定是2^n;

    因此使用tarjan算法求出图中的环,由于题目限制,求点双联通分量和边双联通分量效果相同。因边双联通分量好写,懒惰的作者采用了它 -~_~-

    代码:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cstdlib>
      5 #include<algorithm>
      6 #include<queue>
      7 #include<stack>
      8 #include<vector>
      9 #define ll long long
     10 #define MOD 1000000007
     11 using namespace std;
     12 vector<int>a[100100],b[200100];
     13 stack<int>s;
     14 int n,m,q,mm=0,dep[200100],dfn[100100],low[100100],fa[100100],p[200100][20],color[110100],times,cnt;
     15 bool vis[100100],viss[200100];
     16 void tarjan(int u){
     17     int i,j,child=0,v;
     18     s.push(u);vis[u]=1;
     19     dfn[u]=low[u]=times++;
     20     for(i=0;i<a[u].size();i++){
     21         v=a[u][i];
     22         if(!vis[v]){
     23             child++;fa[v]=u;
     24             tarjan(v);
     25             low[u]=min(low[u],low[v]);
     26         }
     27         else if(v!=fa[u]) low[u]=min(low[u],dfn[v]);
     28     }
     29     if(dfn[u]==low[u]){
     30         cnt++;
     31         int count=0,tmp;
     32         while(!s.empty()){
     33             tmp=s.top();
     34             color[tmp]=cnt+n;
     35             s.pop();count++;
     36             if(tmp==u) break;
     37         }
     38         if(count==1) color[tmp]=tmp;
     39     }
     40 }
     41 void dfs(int u){
     42     int i,v,t1,t2;t1=color[u];
     43     viss[t1]=1;vis[u]=1;
     44     for(i=0;i<a[u].size();i++){
     45         v=a[u][i];
     46         t2=color[v];
     47         if(viss[t2]) goto skip;
     48         mm++;p[t2][0]=t1;dep[t2]=dep[t1]+1;
     49         b[t1].push_back(t2);b[t2].push_back(t1);
     50         skip:if(vis[v]) continue;
     51         dfs(v);
     52     }
     53     return;
     54 }
     55 void init(){
     56     int i,j;
     57     for(j=1;j<20;j++){
     58         for(i=1;i<n+cnt;i++){
     59             if(!b[i].size()) continue;
     60             p[i][j]=p[p[i][j-1]][j-1];
     61         }
     62     }
     63 }
     64 void shrink(){
     65     int i,j;
     66     for(i=1;i<=n;i++){
     67         if(!dfn[i]) tarjan(i);
     68     }
     69     memset(vis,0,sizeof(vis));memset(viss,0,sizeof(viss));
     70     dep[color[1]]=1;p[color[1]][0]=color[1];dfs(1);
     71     init();
     72     return ;
     73 }
     74 ll calc(int k,int to){
     75     if(k==to) return (to>n)?2:1;
     76     else return calc(p[k][0],to)*((k>n)?2:1);
     77 }
     78 ll lca(int l,int r){
     79     int i,lca,tl=l,tr=r;
     80     if(dep[l]<dep[r]) swap(l,r);
     81     int diff=dep[l]-dep[r];
     82     for(i=0;i<20;i++){
     83         if(diff&(1<<i)) l=p[l][i];
     84     }
     85     if(l==r) lca=l;
     86     else{
     87         for(i=19;i>=0;i--){
     88             if(p[l][i]!=p[r][i]){
     89                 l=p[l][i];r=p[r][i];
     90             }
     91         }
     92         lca=p[l][0];
     93     }
     94     return calc(tl,lca)*calc(tr,lca)/((lca>n)?2:1);
     95 }
     96 int main(){
     97     int i,t1,t2;
     98     scanf("%d%d",&n,&m);
     99     for(i=1;i<=n;i++) color[i]=i;
    100     for(i=1;i<=m;i++){
    101         scanf("%d%d",&t1,&t2);
    102         a[t1].push_back(t2);a[t2].push_back(t1);
    103     }
    104     shrink();
    105     scanf("%d",&q);
    106     for(i=1;i<=q;i++){
    107         scanf("%d%d",&t1,&t2);
    108         printf("%lld
    ",lca(color[t1],color[t2])%MOD);
    109     }
    110 }
  • 相关阅读:
    2.16.8.内核启动的C语言阶段5
    2.16.7.内核启动的C语言阶段4
    2.16.6.内核启动的C语言阶段3
    2.16.5.内核启动的C语言阶段2
    JAVA_SE基础——34.static修饰成员变量
    JAVA_SE基础——33.this关键字的练习
    JAVA_SE基础——32.this关键字调用本类的构造方法
    JAVA_SE基础——31.this关键字
    JAVA类的方法调用和变量(全套)
    JAVA_SE基础——30.构造代码块
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/7441389.html
Copyright © 2020-2023  润新知