[bzoj5407]girls——容斥原理+三元环计数

版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/80823279

题目大意:

有一个图,你要选定三个点,保证三个点之间两两无互相连边,当点i<j<k的时候,你获得的收益为Ai+Bj+Ck,求所有符合情况的收益和。

思路:

可以用容斥原理,即所有的情况至少有一对有连边+至少有两对有连边三对都有连边 。

  1. 对于所有的情况,可以考虑每个点来算贡献
  2. 对于至少有一对连边的情况,可以枚举一个点和它的连边,然后用前缀和计算处余下来的所有的点。
  3. 对于至少有两对连边的情况,可以枚举中间那个于两个点都有连边的点,然后对于它连接的每一个点计算贡献
  4. 至于计算三元环,提供两种同样都是O(mm)的方法:第一种即暴力枚举每一条边然后再枚举这条边的度数较少的那个端点的边,然后判断剩下两点之间有无连边。还有一种方法就是对于度数小于m的点枚举两条边,对于度数大于m的点直接n3枚举,由于后面的点之多只有m个,所以总的复杂度为O(mm)
    然后注意在计算的过程中別爆int了。
/*==========================
 * Author : ylsoi
 * Problem : girls
 * Algorithm : counting
 * Time : 2018.6.26
 * ========================*/
#include<bits/stdc++.h>
#include<tr1/unordered_map>

#define REP(i,a,b) for(int i=a;i<=b;++i)
#define DREP(i,a,b) for(int i=a;i>=b;--i)
typedef long long ll;

using namespace std;

void File(){
    freopen("20180625T2.in","r",stdin);
    freopen("20180625T2.out","w",stdout);
}

const int maxn=2e5+10;
int n,m;
unsigned long long de[maxn],A,B,C,ans,suma[maxn],sumb[maxn],sumc[maxn];
vector<int>to[maxn];
tr1::unordered_map<int,bool>G[maxn];

void init(){
    scanf("%d%d",&n,&m);
    cin>>A>>B>>C;
    REP(i,1,m){
        int u,v;
        scanf("%d%d",&u,&v);
        ++u; ++v; ++de[u]; ++de[v];
        to[u].push_back(v);
        to[v].push_back(u);
        G[u][v]=1;
        G[v][u]=1;
    }
    REP(i,1,n)sort(to[i].begin(),to[i].end());
}

void cal(){
    REP(i,1,n){
        ans+=A*(i-1)*((ll)(n-i-1)*(n-i)/2);
        ans+=B*(i-1)*(i-1)*(n-i);
        ans+=C*(i-1)*((ll)(i-1)*(i-2)/2);
    }
    REP(i,1,n){
        suma[i]=suma[i-1]+(i-1)*A;
        sumb[i]=sumb[i-1]+(i-1)*B;
        sumc[i]=sumc[i-1]+(i-1)*C;
    }
    REP(i,1,n){
        int siz=to[i].size()-1;
        unsigned long long cnt0=0,cnt1=0;
        REP(j,0,siz){
            int v=to[i][j];
            v<i ? (++cnt0) : (++cnt1);
            if(v<i){
                ans-=suma[v-1]+B*(v-1)*(v-1)+C*(i-1)*(v-1);
                ans+=A*(v-1)*(siz-j)+B*(v-1)*j;
            }
            else{
                ans-=A*(i-1)*(n-v)+B*(v-1)*(n-v)+sumc[n]-sumc[v];
                ans-=A*(i-1)*(v-i-1)+sumb[v-1]-sumb[i]+C*(v-1)*(v-i-1);
                ans+=C*(v-1)*j+B*(v-1)*(siz-j);
            }
        }
        ans+=A*(i-1)*(cnt1*(cnt1-1)/2)+B*(i-1)*cnt0*cnt1+C*(i-1)*cnt0*(cnt0-1)/2;
    }
}

bool cmp(int _,int __){return de[_]<de[__];}

void find_circle(){
    int qu[maxn];
    REP(i,1,n)qu[i]=i;
    sort(qu+1,qu+n+1,cmp);
    int t=1,qq[3];
    while(t+1<=n && de[qu[t+1]]<=sqrt(m))++t;
    unsigned long long s[4]={0};
    REP(i,1,t){
        int u=qu[i],siz=to[u].size()-1;
        REP(j,0,siz)REP(k,j+1,siz)if(G[to[u][j]][to[u][k]]){
            int cnt=0;
            qq[0]=u; qq[1]=to[u][j]; qq[2]=to[u][k];
            REP(l,0,2)if(de[qq[l]]<=sqrt(m))++cnt;
            sort(qq,qq+3);
            s[cnt]+=(qq[0]-1)*A+(qq[1]-1)*B+(qq[2]-1)*C;
        }
    }
    ans-=s[1]+s[2]/2+s[3]/3;
    REP(i,t+1,n)REP(j,i+1,n)REP(k,j+1,n)
        if(G[qu[i]][qu[j]] && G[qu[j]][qu[k]] && G[qu[i]][qu[k]]){
            qq[0]=qu[i]; qq[1]=qu[j]; qq[2]=qu[k];
            sort(qq,qq+3);
            s[0]+=(qq[0]-1)*A+(qq[1]-1)*B+(qq[2]-1)*C;
        }
    ans-=s[0];
}

int main(){
    File();
    init();
    cal();
    find_circle();
    cout<<ans<<endl;
    return 0;
}
展开阅读全文

没有更多推荐了,返回首页