1. 程式人生 > >[bzoj5407]girls——容斥原理+三元環計數

[bzoj5407]girls——容斥原理+三元環計數

題目大意:

有一個圖,你要選定三個點,保證三個點之間兩兩無互相連邊,當點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; }