zoukankan      html  css  js  c++  java
  • 安全路径 [容斥]

    安全路径


    color{blue}{最初想法}

    感觉是树形dp, 一步错步步错 .
    F[i]F[i] 表示以 ii 点端点在其子树中有多少条符合条件的路径, 且当前枚举到了子树节点 toto,

    发现这样的状态只能 O(N2)O(N^2) 枚举根才能得出正确的答案 .

    • 若边权为 b'b', 则 Ans+=F[k]F[to]Ans+=F[k]*F[to], F[k]+=F[to]F[k] += F[to]
    • 若边权为 r'r', 则 Ans+=F[k]size[to]Ans += F[k]*size[to], F[k]+=size[to]F[k] += size[to].

    然后就交上去了, 发现爆炸 .
    原因是 题目中的三元组 之间的路径 才有限制(死在这里), 不一定成一条链, 可以是下图这个样子,


    所以这个算法已废 .

    样例真水


    color{red}{正解部分}

    可以容斥, 求出 危险的三元组 的个数,
    危险的三元组点与点之间 存在b'b' 路径,
    所以求出所有蓝色的连通块大小 SiS_i,
    Ans=CN3CSi3CSi2(NSi)Ans = C_{N}^3 - sum C_{S_i}^{3} - sum C_{S_i}^2*(N-S_i) .

    十年OI一场空, 看错题目见祖宗 …


    color{red}{实现部分}

    若两个点有蓝边相连, 即可归入同一个连通块, 可以使用并查集实现 .

    #include<bits/stdc++.h>
    #define reg register
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    const int maxn = 50005;
    const int mod = 1e9 + 7;
    
    int N;
    int F[maxn];
    int fac[maxn];
    int size[maxn];
    int rev[maxn];
    
    int Ksm(int a, int b){
    	int s = 1;
    	while(b){
    		if(b & 1) s = 1ll*s*a % mod;
    		a = 1ll*a*a % mod;
    		b >>= 1;
    	}
    	return s;
    }
    
    int C(int n, int m){
            int t1 = fac[n];
            int t2 = 1ll*fac[n-m]*fac[m] % mod;
            return 1ll*t1*Ksm(t2, mod-2) % mod;
    }
    
    int Find(int x){ return F[x]==x?x:F[x]=Find(F[x]); }
    
    int main(){
            N = read();
            for(reg int i = 1; i <= N; i ++) F[i] = i, size[i] = 1;
            for(reg int i = 1; i < N; i ++){
                    char ch[2];
                    int a = read(), b = read();
                    scanf("%s", ch);
                    if(ch[0] == 'b'){
                            int t1 = Find(a), t2 = Find(b);
                            if(t1 != t2) F[t2] = t1, size[t1] += size[t2];
                    }
            }
            fac[0] = 1;
            for(reg int i = 1; i <= N; i ++) fac[i] = 1ll*fac[i-1]*i % mod;
            int Ans = C(N, 3);
            for(reg int i = 1; i <= N; i ++){
                    if(Find(i) != i) continue ;
                    if(size[i] >= 3) Ans = (1ll*Ans - C(size[i], 3) + mod) % mod;
                    if(size[i] >= 2) Ans = (1ll*Ans - (1ll*C(size[i], 2)*(N-size[i])%mod) + mod) % mod;
            }
            printf("%d
    ", Ans);
            return 0;
    }
    
  • 相关阅读:
    接竹竿
    Vijos P1053 Easy SSSP
    计算机网络-五层协议和物理层
    代码阅读
    selenium自动化测试原理和设计的分享
    appium desktop 1.7 byName不能用,重写
    appium desktop 1.7 的swipe功能不能用,重写。
    appium在不同类中使用的是同一个session
    GIT 上传、ssh设置、一些命令。
    java 学习:在java中启动其他应用,由jenkins想到的
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822526.html
Copyright © 2011-2022 走看看