FZOJ191. 「2019冬令营提高组」子图(三元环计数,四元环计数)

题目大意:
n n n 个点, m m m 条边的无向图,询问含有 k k k 条边的联通子图的个数
n ≤ 1 0 5 , m ≤ 2 ∗ 1 0 5 , k ≤ 4 n\le10^5,m\le2*10^5,k\le4 n105,m2105,k4


k k k 很小,考虑大力讨论
只要把所有情况画出来并且找到相互关系即可
需要在 m m m\sqrt{m} mm 的时间内实现找三元环和四元环(常数还得小)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
#define pb push_back
#define SZ(x) ((int)(x.size()))
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	// input a signed integer
	template <class I>
	inline void gi (I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;
	}
	// print a signed integer
	template <class I>
	inline void print (I &x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
	}
	//no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int p = 1e9+7;
int n,m,k,du[101000];
int linkk[101000],t;
int g[10],in[101000]; 
int cnt[101000],tmp[101000],tot;
vector<int>G[101000],T[101000];
struct line{int l,r;}q[201000];
struct node{int n,y;}e[401000];
inline int ksm(int a,int x){int now = 1;for(;x;x>>=1,a=1ll*a*a%p) if(x&1) now=1ll*now*a%p;return now;}
inline int mul(int a,int b){return 1ll*a*b%p;}
inline int del(int a,int b){return (a-b+p)%p;}
inline void add(int &a,int b){a += b;if(a >= p) a -= p;}
inline int calc(int a,int b){return (a+b)%p;}
inline void insert(int x,int y){
	e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;du[x]++;T[x].pb(y);
	e[++t].y = x;e[t].n = linkk[y];linkk[y] = t;du[y]++;T[y].pb(x);
}
bool mycmp(int a,int b){return du[a]<du[b] || (du[a]==du[b] && a<b);}
int F1(int x){
	int now = 0;
	rep(i,1,n) if(x == 3) now = calc(now,mul(du[i],mul(du[i]-1,mul(du[i]-2,ksm(6,p-2)))));
	else if(x == 4) now = calc(now,mul( du[i] , mul(du[i]-1 , mul( du[i]-2 , mul( du[i]-3, ksm(24,p-2) ) ) ) ));
	return now;
}
int pre[101000];
int F2(){
	int now = 0;
	rep(x,1,n){
		rep(i,0,SZ(G[x])-1) pre[G[x][i]] = x;
		rep(i,0,SZ(G[x])-1){
			int y = G[x][i];
			rep(j,0,SZ(G[y])-1) if(pre[G[y][j]] == x) now++,in[x]++,in[y]++,in[G[y][j]]++;
		}
	}
	return now;
}
int G1(){
	int now = 0,tot = 0;
	rep(x,1,n){
		tot = 0;
		for(int i = linkk[x];i;i = e[i].n)
		    now = del(now,mul(du[e[i].y]-1,du[e[i].y]-1)),tot += du[e[i].y]-1;
		now = calc(now,mul(tot,tot));
	}
	return mul(now,ksm(2,p-2));
}
int G3(){
	int now = 0;
	rep(i,1,n) now = calc(now,mul(in[i],du[i]-2));
	return now;
}
int G4(){
	int now = 0;
	rep(i,1,m) {
		int x = q[i].l,y = q[i].r;du[x]--;du[y]--;
		now = calc(now,mul( mul( du[x]%2?du[x]:du[x]/2  , du[x]%2?(du[x]-1)/2:du[x]-1  ) , du[y] ));
		now = calc(now,mul( mul( du[y]%2?du[y]:du[y]/2  , du[y]%2?(du[y]-1)/2:du[y]-1  ) , du[x] ));
		du[x]++;du[y]++;
	}
	return now;
}
int ID[101000];
bool vis[101000];
int G5(){
	int now = 0;
	rep(x,1,n) sort(G[x].begin(),G[x].end(),mycmp),sort(T[x].begin(),T[x].end(),mycmp);
	rep(x,1,n){
		tot = 0;sort(G[x].begin(),G[x].end(),mycmp);
		rep(i,0,SZ(G[x])-1){
			int y = G[x][i];
			repp(j,SZ(T[y])-1,0) if(du[T[y][j]] > du[x] || (du[T[y][j]] == du[x] && T[y][j] > x)){
				add(now,cnt[T[y][j]]);cnt[T[y][j]]++;
				if(!vis[T[y][j]]) vis[T[y][j]] = true,tmp[++tot] = T[y][j];
			}
			else break;
		}
		rep(i,1,tot) vis[tmp[i]] = false,cnt[tmp[i]] = 0;
	}
	return now;
}
void work1(){
	int ans = 0,now = F1(3),tmp;
	rep(x,1,n) for(int i = linkk[x];i;i = e[i].n) add(ans,mul(du[e[i].y]-1,du[x]-1));
	tmp = F2(); ans = del(ans,mul(6,tmp)); ans = mul(ans,ksm(2,p-2)); ans = calc(ans,calc(tmp,now));
	printf("%d\n",ans);
}
void work2(){
	g[2] = F1(4);g[6] = F2();g[3] = G3();g[4] = G4();
	g[4] = del(g[4],mul(2,g[3]));g[5] = G5();
	g[1] = G1(); g[1] = del(g[1],calc( mul(4,g[5]) , calc( mul(2,g[3]) , mul(3,g[6]) ) ));
	int ans = calc(g[1],calc(g[2],calc(g[3],calc(g[4],g[5]))));
	printf("%d\n",ans);
}
int main() {
	freopen("subgraph.in","r",stdin);
	freopen("subgraph.out","w",stdout);
	gi(n); gi(m); gi(k);int x,y;
	rep(i,1,m) gi(x),gi(y),insert(x,y),q[i].l = x,q[i].r = y;
	rep(x,1,n) for(int i = linkk[x];i;i = e[i].n) if(du[e[i].y] > du[x] || (du[e[i].y] == du[x] && e[i].y > x)) G[x].pb(e[i].y);
	if(k == 3) work1();
	else work2();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值