首页 > 代码库 > 第48套题【tarjan】【图&树的连通性】【并查集】

第48套题【tarjan】【图&树的连通性】【并查集】

Problem 1 图的连通性
??题目背景??
琼和雪不知从什么时候就开始形影不离得呆在一起,无话不说了
那天她们在谈论图论
??题意描述??
“有一个无向图,每次断掉一条边,并询问两个点时候联通,你会维护么?
” 琼很认真地问。
“为什么要知道这个呢?”
“我们总要知道自己是否身陷囹囵……你必须立刻告诉我答案哦”
??输入格式??
测试数据的第一行是三个正整数n、m、t, 表示无向图有n 个点,m 条边
和t 个操作
接下来m 行,每行两个正整数x、y,表示x 和y 之间有一条边(允许存
在重边)
接下来t 行,每行三个整数w x y,w=1 时表示将x 和y 之间的边剪断
(保证存在这样一条边)。w=2 时表示查询x 与y 时候联通,是输出1, 否则输
出0。
为了保证强制在线,最后t 行输入数据中的x 和y 必须要??或(C/C++ 中的^ ??
算,PASCAL 中的xor)执行当??操作之??图中????的边数,??能得到实际问题中的
x 和y
??输出格式??
对于每次询问,输出一行,一个数,1 或0,1 表示联通,0 表示不联通。
??样例输入??
5 9 6
1 2
2 3
1 3
1 3
3 1
1 5
1 4
3 5
5 4
1 12 13
2 13 12
1 9 12
2 3 2
1 4 6
2 7 5
??样例输出??
1
0
1
??样例说明??
6 个操作依次为:
1. 断掉5 和4 之间的边
2. 查询5 和4 之间的连通性
3. 断掉1 和4 之间的边
4. 查询5 和4 之间的连通性
5. 断掉1 和3 之间的边
6. 查询1 和3 之间的连通性
??数据规模和约定??
对于40% 的数据,n <=100; m <= 1000; t <= 500
对于100% 的数据,n <= 100000; m <= 150000; t  <=100000


Problem 2 树的连通性
??题目背景??
雪的眉头紧缩
“是不是那个问题太困难了?” 琼问到。
雪点了点头
“那好,我们简化一下问题,假设图保证联通,没有重边,也没有环,重复
刚刚的问题,是不是简单一些?”
雪的眼中冒着希望的光芒
“你还是要立刻告诉我答案哦。” 琼笑了
??题意说明??
给定一个n 个点的无向图,保证联通且无环无重边,每个点上有一个可修
改的权值,每次断掉一条边、修改某个节点上的权值或询问两个点之间的连通
性。
??输入格式??
输入数据的第一行是两个数N、M,点的个数和操作的个数
接下来N 行,每行一个正整数,表示每个点上的初始权值
接下来若干行,每行两个数x 和y,表示x 和y 之间有一条无向边
接下来M 行,每行三个数t x y,含义如下:
t=1 将x 和y 之间的边断开(保证存在)
t=2 查询x 和y 是否联通,若联通,输出他们权值的乘积,否则输出他
们权值的和
t=3 将点x 上的权值修改为y
为了保证算法强制在线,设上一次查询的结果为lastans,则最后t 行输入
数据的x 和y 的实际值= 输入值xor lastans, lastans 的初始值为0
??输出格式??
对于每个查询操作,输出一行一个数,要求如上所示
??样例输入??
8 5
1
2
3
4
5
6
7
8
1 2
1 5
4 2
3 2
5 7
5 6
8 5
1 5 1
2 4 6
2 11 9
3 0 15
2 0 11
??样例输出??
10
3
20
??样例说明??
先断开1 和5 之间的边,然后查询4 和6 的连通性。由于不联通,所以答
案是10
然后查询1 和3 的连通性,由于联通,所以答案是3
然后将3 号节点上的数值修改为12
最后查询3 和8 的连通性。由于不联通,所以答案是它们的权值和20
??数据规模和约定??
对于10% 的数据,没有断边操作
对于40% 的数据,n  1000; m  500
对于100% 的数据,n  200000; m  200000,0 < 点上的权值 1000


Problem 3 逻辑的连通性
??题目背景??
“我已经快疯了,是时候整理一下心中的逻辑了。” 雪说着,打开了《高中
数学·选修2-1》
??题意说明??
假如有命题p 一定能推出命题q,则称p 是q 的充分条件,q 是p 的必要
条件。
特别的,当p 既是q 的充分条件,又是q 的必要条件时,称p 和q 互为
充要条件
现在有n 个命题,其中一些是另一些的充分条件。请问有多少对命题互为
充要条件?
??输入格式??
第一行三个正整数n,m,t,分别表示命题数、已知关系数和询问数
接下来m 行,每行两个正整数p 和q,表示命题p 是命题q 的充分条件
??输出格式??
仅一行,一个整数,表示充要条件的对数
??样例输入??
5 5
1 3
3 2
2 1
4 5
5 4
??样例输出??
4
??样例说明??
4 对充要条件分别是(1, 2)、(2, 3)、(1, 3)、(4, 5)
??数据规模和约定??
对于10% 的数据,n  <=10;m  <=50
对于40% 的数据,n <= 500;m <= 1000
对于另外10% 的数据,数据中保证没有重边且m = n^2
对于100% 的数据,n  <=50000;m  <=600000


 

解题报告:参考题解:http://blog.csdn.net/ourfutr2330/article/details/52744358

  第一题:考试的时候乱写的暴力,还顺便复制给第二题用,结果无向边又忘开两倍了。。于是崩了。正解:其实不是强制在线,可以先把所有要删的边都删了,然后再一条一条的加上去,用并查集的思想,合并,查询的时候用find()就对了。注意:输出的时候答案一定要倒序输出,因为是倒着处理的每个操作;

  删边:

void cut(int a,int b)//删边 {    int v=he[a];    if (to[v]==b) {//在第一条边        he[a]=ne[v];        //v=ne[v];    }    else while (ne[v]){//在中间        if (to[ne[v]]==b) {//  不是 to[v],因为要跳过第一条边            ne[v]=ne[ne[v]];                return ;        }        v=ne[v];    }}

  然后并查集的一些基本操作再复习一下:

 void unionn(int u,int v)//合并 {      fa[v]=u;  }    {      int r1=find(x);       int  r2=find(y) ;      if (r1!=r2) unionn(r1,r2);//注意合并的是r1,r2 ,不是x,y        }
1 int find(int u)2 {3     if (fa[u]!=u) return fa[u]=find(fa[u]);//全部改他们的 fa  4     return fa[u];5 }

  代码:

技术分享
  1 #include<iostream>  2 #include<cstdio>  3 #include<cstring>  4 #include<queue>  5 #define maxn 100005  6 #define inf 300005  7 using namespace std;  8 int m,n,t,delt,fa[maxn],ans[maxn],hh;  9 int tot,ne[inf],he[maxn],to[inf]; 10 bool vis[maxn]; 11 struct pp{ 12     int ti,x,y; 13 }; 14 pp qus[maxn]; 15 void add(int a,int b) 16 { 17     tot++;ne[tot]=he[a];to[tot]=b;he[a]=tot; 18 } 19 int find(int u) 20 { 21     if (fa[u]!=u) return fa[u]=find(fa[u]);//全部改他们的 fa   22     return fa[u]; 23 } 24 void unionn(int u,int v) 25 { 26     fa[v]=u; 27 } 28 void cut(int a,int b)//删边  29 { 30     int v=he[a]; 31     if (to[v]==b) { 32         he[a]=ne[v]; 33         //v=ne[v]; 34     } 35     else while (ne[v]){ 36         if (to[ne[v]]==b) {//ne[v] 37             ne[v]=ne[ne[v]];     38             return ; 39         } 40         v=ne[v]; 41     } 42 } 43 int main() 44 { 45     freopen("graph.in","r",stdin); 46     //freopen("graph.out","w",stdout); 47     cin>>n>>m>>t; 48     for (int i=1;i<=m;i++) 49     { 50         int a,b; 51         scanf("%d%d",&a,&b); 52         add(a,b); 53         add(b,a); 54     } 55     delt=m; 56     for (int i=1;i<=t;i++) 57     { 58         int tt=i; 59         scanf("%d%d%d",&qus[tt].ti,&qus[tt].x,&qus[tt].y); 60         if (qus[tt].ti==1) { 61             qus[tt].x^=delt;qus[tt].y^=delt; 62             cut(qus[tt].x,qus[tt].y); 63             cut(qus[tt].y,qus[tt].x);//先全部删掉  64             delt--; 65         } 66         else { 67             qus[tt].x^=delt;qus[tt].y^=delt; 68         } 69     } 70     for (int i=1;i<=n;i++) 71       fa[i]=i; 72     for (int i=1;i<=n;i++) 73     { 74         int v=he[i]; 75         while (v) 76         { 77             if (find(i)!=find(to[v]))  78             { 79               unionn(fa[i],fa[to[v]]);//修改fa的fa而不是当前点的fa  80             } 81             v=ne[v]; 82         } 83     } 84     for (int i=t;i>=1;i--) 85     { 86         int vi,vj; 87         vi=find(qus[i].x); 88         vj=find(qus[i].y);  89         if (qus[i].ti==1) { 90             //add(qus[i].x,qus[i].y);add(qus[i].y,qus[i].x); 91             if (vi!=vj) unionn(vi,vj);//修改find(x)不是 x   92         } 93         else{    94             hh++;                95             if (vi==vj) 96             ans[hh]=1;  97         } 98     } 99     for(int i=hh;i>=1;i--)100     {101         printf("%d\n",ans[i]);//倒序输出答案 102     }103     return 0;104 }
View Code

第二题:

  利用它是一棵树的性质,可以修改是另一个点的儿子的点,将它的下面的所有儿子点修改一个值 f[i] ,这样可以保证每个不连通的点的 f 值都不一样,查询的时候判断一下 f 数组就可以了。但是一定要注意,删掉这条边还是必要的,因为会对下面的询问有影响,不删的话,修改的时候就会多修改一些点。

  

技术分享
 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #define maxn 200005 6 using namespace std; 7 int n,t,lastans,va[maxn],f[maxn],cnt,fa[maxn]; 8 int tot,ne[maxn*2],he[maxn],to[maxn*2]; 9 void add(int a,int b)10 {11     tot++;ne[tot]=he[a];to[tot]=b;he[a]=tot;12 }13 void dfs(int x)14 {15     int u=he[x];16     f[x]=cnt;17     while (u)18     {19         if (to[u]!=fa[x])//向下建树 20         {21             fa[to[u]]=x;22             dfs(to[u]);23         }24         u=ne[u];25     }26 }27 void cut(int a,int b)28 {29     int v=he[a];30     while (to[v]==b)//要删的边在head开头 31     {32         he[a]=ne[v];33         v=ne[v];34         return ;35     }36     while (ne[v])//要删的边在中间 37     {38         if (to[ne[v]]==b)39           {40               ne[v]=ne[ne[v]];41               return ; 42           }43         v=ne[v];44     }45 }46 int main()47 {48     freopen("tree.in","r",stdin);49     freopen("tree.out","w",stdout);50     cin>>n>>t;51     for (int i=1;i<=n;i++)52       scanf("%d",&va[i]);53     for (int i=1;i<=n-1;i++)54     {55         int a,b;56         scanf("%d%d",&a,&b);57         add(a,b);58         add(b,a);59     }60     dfs(1);61     for (int i=1;i<=t;i++)62     {63         int x,y,z;64         scanf("%d%d%d",&z,&x,&y);65         x=(lastans^x); y=(y^lastans);66         if (z==1) {67                 cut(x,y);68                 cut(y,x);69                 cnt++;70             if (x==fa[y]) dfs(y);71             else dfs(x);72         }73         else if (z==2){74             if (f[x]==f[y]) lastans=va[x]*va[y];75             else lastans=va[x]+va[y];76             cout<<lastans<<endl;77         }78         else if (z==3){79             va[x]=y;80         }81     }82     return 0;83 }
View Code

第三题:

  一秒 tarjan 。还是要多复习啊。只不过我的tarjan怎么这么慢。。。

技术分享
 1 #include<iostream> 2 #include<cstdio> 3 #include<map> 4 #include<cstring> 5 #define maxn 50005 6 #define inf 600005 7 #define ll long long 8 using namespace std; 9 int n,m,s[maxn],dfn[maxn],low[maxn],init,num;10 int tot,he[maxn],ne[inf],to[inf];11 ll ans;12 bool vis[maxn],in[maxn];13 void add(int a,int b)14 {15     tot++;ne[tot]=he[a];to[tot]=b;he[a]=tot;16 }17 void tarjan(int x)18 {19     dfn[x]=low[x]=++num;20     s[++init]=x;21     in[x]=true;22     for (int i=he[x];i;i=ne[i])23     if (!vis[to[i]]){24         if (!dfn[to[i]]){25             tarjan(to[i]);26             if (low[to[i]]<low[x]) 27                 low[x]=low[to[i]];28         }29         if (in[to[i]]&&low[x]>dfn[to[i]]) 30             low[x]=dfn[to[i]];31     }32     int u;33     ll p=0;34     if (dfn[x]==low[x]) {35         do36         {37             u=s[init--];38             in[u]=false;39             vis[u]=true;40             p++;41         }while (u!=x);42         ans+=p*(p-1)/2;43     }44 }45 int main()46 {47     freopen("logic.in","r",stdin);48     freopen("logic.out","w",stdout);49     cin>>n>>m;50     for (int i=1;i<=m;i++)51     {52         int x,y;53         scanf("%d%d",&x,&y);54         add(x,y);55     }56     for (int i=1;i<=n;i++)57         if (!vis[i]) {58             memset(in,0,sizeof (in));59             memset(s,0,sizeof (s));60             memset(dfn,0,sizeof (dfn));61             memset(low,0,sizeof (low));62             tarjan(i);    63         }64     cout<<ans;65     return 0;66 }
View Code

  国庆要完了,我在学校里面都三天了。。作业还没做完。。今天开夜车哇。

  

 

第48套题【tarjan】【图&树的连通性】【并查集】