zoukankan      html  css  js  c++  java
  • Codechef TAPAIR Counting the important pairs 随机化、树上差分

    传送门

    题意:给出一个$N$个点、$M$条边的无向连通图,求有多少组无序数对$(i,j)$满足:割掉第$i$条边与第$j$条边之后,图变为不连通。$N leq 10^5 , M leq 3 imes 10^5$


    竟然随机化,歪果仁的思想好灵活qwq肯定是数据结构做多了

    看起来很像割边,考虑$tarjan$,但是边三连通分量并不是很好实现,而且有很多特殊情况需要判断,所以我们考虑另外的算法

    考虑$tarjan$时建出的一棵树。对于它来说,在一个端点在其下方、另一个端点在其上方的的返祖边可以成为它的依靠,因为割掉它,这一条依靠边可以代替它的功能。而对于一条返祖边来说,割掉对于树边是没有影响的,我们就定义它自己为自己的依靠。

    这样每一条边都有自己的依靠集合。考虑两条依赖集合相同的边,将它们割掉之后,中间一段的点就会因为上下都没有额外的依靠而使得图不连通。而对于一条依赖集合为空的边(即割边),它选择任何边都可以加入贡献。

    所以我们现在需要考虑如何给某一段边加入贡献。然后CC的题解给出的玄学办法是:随机化+树上差分+XOR

    我们考虑将给一条返祖边定权值为一个$random$出来的值$t$,然后把所有依靠它的边的依靠集合异或上这个值,这个可以树上差分去做。这样所有的依靠集合就变成了一个数。然后我们判断两条边的依靠集合对应的数是否相等即可。

    Because 2^64 is large enough comparing to the range of N and M, don't worry about the probability. :) You will get AC if you implemented correctly.——原题题解

    然而我$rand() WA$了好几发qwq

    如果随机数种子出了问题的话就看下面这种玄学rand好了

     1 #include<bits/stdc++.h>
     2 #define CC
     3 using namespace std;
     4 
     5 inline int read(){
     6     int a = 0;
     7     bool f = 0;
     8     char c = getchar();
     9     while(c != EOF && !isdigit(c)){
    10         if(c == '-')
    11             f = 1;
    12         c = getchar();
    13     }
    14     while(c != EOF && isdigit(c)){
    15         a = (a << 3) + (a << 1) + (c ^ '0');
    16         c = getchar();
    17     }
    18     return f ? -a : a;
    19 }
    20 
    21 const int MAXN = 100010;
    22 struct Edge{
    23     int end , upEd;
    24 }Ed[MAXN * 6];
    25 int head[MAXN] , num[MAXN] , dep[MAXN] , fa[MAXN] , N , cntEd , cnt;
    26 unsigned long long point[MAXN] , forS[MAXN * 3];
    27 bool vis[MAXN];
    28 
    29 #define ll unsigned long long
    30 inline ll rp(){return (1ll*rand())^(1ll*rand())<<15^(1ll*rand())<<30^(1ll*rand())<<45^(1ll*rand())<<60;}
    31 
    32 inline void addEd(int a , int b){
    33     Ed[++cntEd].end = b;
    34     Ed[cntEd].upEd = head[a];
    35     head[a] = cntEd;
    36 }
    37 
    38 void dfs1(int x , int t){
    39     fa[x] = t;
    40     dep[x] = dep[fa[x]] + 1;
    41     vis[x] = 1;
    42     for(int i = head[x] ; i ; i = Ed[i].upEd)
    43         if(!vis[Ed[i].end])
    44             dfs1(Ed[i].end , x);
    45         else
    46             if(dep[Ed[i].end] > dep[x]){
    47                 long long t = rp();
    48                 point[x] ^= t;
    49                 point[Ed[i].end] ^= t;
    50                 forS[++cnt] = t;
    51             }
    52 }
    53 
    54 void dfs2(int x){
    55     for(int i = head[x] ; i ; i = Ed[i].upEd)
    56         if(fa[Ed[i].end] == x){
    57             dfs2(Ed[i].end);
    58             point[x] ^= point[Ed[i].end];
    59         }
    60     if(x != 1)
    61         forS[++cnt] = point[x];
    62 }
    63 
    64 int main(){
    65 #ifdef CC
    66     freopen("TAPAIR.in" , "r" , stdin);
    67     freopen("TAPAIR.out" , "w" , stdout);
    68 #endif
    69     
    70     N = read();
    71     int M = read();
    72     for(int i = 1 ; i <= M ; i++){
    73         int a = read() , b = read();
    74         addEd(a , b);
    75         addEd(b , a);
    76     }
    77     dfs1(1 , 0);
    78     dfs2(1);
    79     sort(forS + 1 , forS + cnt + 1);
    80     int p = 1;
    81     while(p <= cnt && forS[p] == 0)
    82         p++;
    83     long long ans = (p - 1) * (long long)(p - 2) / 2 + (p - 1) * (M - p + 1);
    84     while(p <= cnt){
    85         int beg = p;
    86         while(p <= cnt && forS[p] == forS[beg])
    87             p++;
    88         ans += (long long)(p - beg) * (p - beg - 1) / 2;
    89     }
    90     cout << ans;
    91     return 0;
    92 }
  • 相关阅读:
    UiAutomator自动化测试框架介绍
    mongkeyrunner实现循环随机输入值的方法
    python出输出字符串方式:
    Python之字符串小代码解析
    安装JDK,Python SDK及环境变量的配置
    Monkeyrunner小脚本关于camera的使用
    ubuntu 下安装32位库 ia32-libs方法
    关于monkeyrunner的一些初步理解性的题目
    基于redis的限流
    表单防重复提交
  • 原文地址:https://www.cnblogs.com/Itst/p/9858885.html
Copyright © 2011-2022 走看看