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

题目大意:

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

思路:

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

  1. 对于所有的情况,可以考虑每个点来算贡献
  2. 对于至少有一对连边的情况,可以枚举一个点和它的连边,然后用前缀和计算处余下来的所有的点。
  3. 对于至少有两对连边的情况,可以枚举中间那个于两个点都有连边的点,然后对于它连接的每一个点计算贡献
  4. 至于计算三元环,提供两种同样都是O(mm)的方法:第一种即暴力枚举每一条边然后再枚举这条边的度数较少的那个端点的边,然后判断剩下两点之间有无连边。还有一种方法就是对于度数小于 m m 的点枚举两条边,对于度数大于 m m 的点直接 n3 n 3 枚举,由于后面的点之多只有 m m 个,所以总的复杂度为 O(mm) O ( m m )
    然后注意在计算的过程中別爆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;
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值