【51nod1253]クンドゥツリー(互いに素なセットボリューム撥+)

問題の表面を見るにはここをクリック

一般的な問題の意味:あなたのツリーを与えるために、それぞれの側が黒色又は赤色である、必要なトリプレットの数\((X、Y、Z)\) その結果、パス\((X、Y)、 (X、 z)は、(Y、Z) \) 赤側の少なくとも一方が存在します。

インクルージョン排除

私たちは、使用することができます含める排除アイデアを、マイナスのプログラムの合計数と違法スキームの数は、あなたが法的なプログラムの数を取得することができます。

無効なプログラムは、パスすることが必要である\((X、Y)、 (X、Z)は、(Y、Z)\) 少なくとも一つのパスが全て黒です。

我々は唯一の黒側を残して、木の赤い側を削除した場合。これは、同じブロック内での通信中に少なくとも2点の存在を満たすために不正なプログラムを発見することができます。

答えを計算

考えてみましょう互いに素セット、ブロックごとの通信統計ドット\(S_Iを\)

その後、我々は、通信ブロックを列挙する\(I \)ので、少なくとも二つの通信ポイントは、議論は、2つのカテゴリに分けることができ、このブロック内に存在があること。

  • この第三のポイント通信ブロックにおいて、プログラム番号\(S_I C_ {2} ^ \ CDOT(N - S_I)\)
  • 第三の点は、通信ブロック、プログラム番号にない\(S_I C_ {} ^ \ 3)

これは違法なのスキームの数を計算します。

最後に、\(C_N ^ 3 \)マイナス違法なスキームの数が答えです。

コード

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
#define X 1000000007
using namespace std;
int n,s[N+5],fa[N+5];
I int getfa(CI x) {return fa[x]^x?fa[x]=getfa(fa[x]):x;}
int main()
{
    RI i,x,y,t=0;char op;for(scanf("%d",&n),i=1;i<=n;++i) fa[i]=i;//初始化并查集
    for(i=1;i^n;++i) scanf("%d%d",&x,&y),cin>>op,op=='b'&&(x=getfa(x))^(y=getfa(y))&&(fa[x]=y);//只留黑色边
    for(i=1;i<=n;++i) ++s[getfa(i)];for(i=1;i<=n;++i) i==getfa(i)&&//统计连通块点数,枚举连通块算答案
        (t=((1LL*s[i]*(s[i]-1)>>1)%X*(n-s[i])+1LL*s[i]*(s[i]-1)%X*(s[i]-2)%X*(X+1)/6+t)%X);//分两类情况统计不合法方案数
    return printf("%d",(1LL*n*(n-1)%X*(n-2)%X*(X+1)/6-t+X)%X),0;//容斥求出合法方案数
}

おすすめ

転載: www.cnblogs.com/chenxiaoran666/p/51nod1253.html