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         do
36         {
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

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

  

  2016 10 07

  今天老师说不考试,都在看讲义,突然又告诉我们,今天用两个小时再把昨天的这套题写一遍,测一遍。。然后写的时候发现了一些问题,主要是细节问题。

  第一题:问题最多。首先是第一次并集合:

1 for (int i=1;i<=n;i++)
2     {
3         int rr=find(i);
4         for (int j=he[i];j;j=ne[j])
5         {
6             if (find(to[j])!=rr) unionn(fa[i],fa[to[j]]);    //if (judge)因为find才是找的真正的father 
7         }                                                   //而unionn的赋值不是赋值的真正的father所以要判断 
8     }

然后是cut函数:

 1 void cut(int x,int y)
 2 {
 3     int v=he[x];
 4     if (to[v]==y) he[x]=ne[v];
 5     else while (ne[v]){//ne[v]  not to[ne[v]]但是用to[ne[v]]不会错,因为只是多跑了一次 
 6         if (to[ne[v]]==y) {
 7             ne[v]=ne[ne[v]];
 8             return ;
 9         }
10         v=ne[v];
11     }
12 }

第二题:

 1 void dfs(int x)
 2 {
 3     int v=he[x];
 4     f[x]=cnt;//每次进入要修改自己 
 5     while (v)
 6     {
 7         if (to[v]!=fa[x]){
 8             fa[to[v]]=x;
 9             dfs(to[v]);
10         }
11         v=ne[v];
12     }
13 }

第三题:没有赋初值。。。

 1 void tarjan(int x)
 2 {
 3     dfn[x]=low[x]=++num;
 4     in[x]=true;
 5     s[++init]=x;
 6     for (int i=he[x];i;i=ne[i])
 7     if (!vis[to[i]]){
 8         if (!dfn[to[i]]){
 9             tarjan(to[i]);
10             if (low[to[i]]<low[x])
11               low[x]=low[to[i]];
12         }
13         if (in[to[i]]&&dfn[to[i]]<low[x])
14           low[x]=dfn[to[i]];
15     }
16     long long p=0;//赋初值 
17     if (low[x]==dfn[x])
18     {
19         int j;
20         do{
21             j=s[init];
22             in[j]=false;p++;
23             init--;
24         }while (j!=x);
25         ans+=p*(p-1)/2;
26     }
27 }

 

 posted on 2016-10-06 21:26  qzgxlx  阅读(518)  评论(0编辑  收藏  举报