HDU 5072 Coprime (單色三角形問題+容斥原理)

bigbigship發表於2014-11-03

我們先來介紹一下單色三角形問題,如下

單色三角形

在空間中給出了n個點。這些點任三點不共線,並且每兩個點之間都有一條線相連,每一條線不是紅的就是黑的。在這些點和線組成的三角形中,如果一個三角形的三條邊的顏色都相同,那麼我們就稱這個三角形為單色三角形。現給出所有塗紅色的線,試求出單色三角形的數目。

任務:

請寫一個程式:

從文字檔案中讀入點數和對紅色連線的描述;

找出該圖中紅色三角形的數目;

把結果輸出到檔案TRO.OUT中。

輸入格式:

在文字檔案TRO.IN的第一行包括一個整數n,3 <= n <= 1000,為空間中的點數。

該檔案的第二行為一個整數m0 <= m <= 250000,為紅色連線的數目。

以下的m行中每行為兩個用空格分開的整數pk1 <= p < k <= n,表示第p點和第k號點之間的連線為紅色。

輸出格式:

你應該在文字檔案TRO.OUT輸出唯一的一個整數——同色三角形的數目。

樣例:

輸入

6
9
1 2
2 3
2 5
1 4
1 6
3 4
4 5
5 6
3 6

輸出

2
分析:

對於任意的一個不是同色三角形的三角形,他必有一個頂點連著兩條不同色的邊,

因此我們可以考慮統計部同色的三角形有多少個,即統計所有頂點連著不同色的邊

的個數:設d[i]表示第i的頂點連著的紅邊的個數 那麼它連著的黑邊的個數為(n-1-d[i])

sum=d[i]*(n-1-d[i])  (i=1,2,3,....n)

由於沒條邊都統計了兩次所以同色三角形的個數為  ANS = C(n,3) - sum/2;

程式碼如下:

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int maxn = 200010;
typedef long long LL;

int a[maxn];
int cnt[maxn];
int n,num;
int ele[100];

void fen(int x)//素因子分解
{
    num=0;
    for(int i=2;i*i<=x;i++){
        if(x%i==0){
            ele[num++]=i;
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1) ele[num++]=x;
}

void init()//預處理與a[i]不互質的數的個數
{
    memset(cnt,0,sizeof(cnt));
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        fen(a[i]);
        for(int j=1;j<(1<<num);j++){
            int tmp=1;
            for(int k=0;k<num;k++)
                if((1<<k)&j) tmp*=ele[k];
            cnt[tmp]++;
        }
    }
}

LL solve()
{
    LL ans=n;
    ans=ans*(n-1)*(n-2)/6;//防止爆int
    LL sum=0;
    for(int i=0;i<n;i++){//容斥原理求與a[i]不互質的數的個數
        fen(a[i]);
        LL tmp=0;
        for(int j=1;j<(1<<num);j++){
            LL ret = 1;
            int t=0;
            for(int k=0;k<num;k++){
                if((1<<k)&j){
                    ret*=ele[k];
                    t++;
                }
            }
            if(t&1) tmp+=cnt[ret];
            else tmp-=cnt[ret];
        }
        if(tmp==0) continue;
        else sum+=(n-tmp)*(tmp-1);
        //cout<<i<<" "<<tmp<<" "<<(n-tmp)*(tmp-1)/2<<endl;
    }
    return ans-sum/2;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        init();
        LL ans=solve();
        printf("%I64d\n",ans);
    }
    return 0;
}


相關文章