Codeforces 985G - Team Players(三元环)

Codeforces 题目传送门 & 洛谷题目传送门

真·ycx 做啥题我就做啥题

考虑枚举 \(j\),我们预处理出 \(c1_i\) 表示与 \(i\) 相连的编号 \(<i\) 的点的个数,再预处理出 \(c2_i\) 表示与 \(i\) 相连的编号 \(>i\) 的个数,那么共有 \(j-c1_j\)\(<j\) 的数可以成为 \(i\),有 \(n-1-c2_j\) 个数可以成为 \(k\),贡献就随便算一下就行了。

但是这样会多算,多算的部分就是 \(i,k\) 有边相连,但 \((i,j),(k,j)\) 均无边相连的部分。考虑另外减去这一部分的贡献,这一次我们枚举有边相连的 \((i,k)\)——显然这一部分复杂度是 \(\mathcal O(m)\) 的,并且钦定 \(i<k\),那么显然对于所有多算的 \(j\),必然有 \((i,j),(k,j)\) 均无边,我们记这样符合要求的 \(j\)\(c\) 个,它们的和为 \(s\),那么多算的贡献就是 \(cAi+cCk+sB\),减一下就行了。

不过到这里有一个问题,那就是符合条件的 \(j\) 无法直接计算,因为这里的 \((i,k)\) 不独立,要是能拆开来计算就好了,可这里拆不掉。考虑有个东西叫做正难则反,我们考虑拿所有符合要求的 \(j\) 减去 \((i,j),(k,j)\) 至少一个有边的 \(j\) 即可。不过到这里问题又来了,那就是这东西还是不好维护,继续考虑容斥原理,这东西又等价于 \((i,j)\) 有边的 \(j\) \(+\) \((k,j)\) 有边的 \(j\),扣掉 \((i,j),(k,j)\) 都有边的 \(j\),而前面这两个东西恰好是可以分开来算的,相当于我们要分别求对于固定的 \(i,k\),满足 \((i,j)\) 之间存在边,并且 \(i<j<k\)\(j\) 的个数及和;以及 \((k,j)\) 之间存在边,并且 \(i<j<k\)\(j\) 的个数及和——拿前一部分举例,我们对于每个 \(i\) 建一个 std::vector<int> \(g2_i\),维护所有 \(j>i,(i,j)\) 之间有边的 \(j\) 并将其排个序,再建另一个 std::vector<int> \(s2_i\) 维护 \(g2_i\) 的前缀和,然后在 \(g2_i\)std::lower_bound 找出 \(k\) 的位置,查遍前缀和即可。

最后考虑 \((i,j),(k,j),(i,k)\) 都有边的 \(i,j,k\) 的贡献,这是一个非常经典的问题,叫「三元环计数」。然鹅 wtcl 在做这题之前还不会这个科技,所以感谢这个题让我学会了这玩意儿。考虑对原图中每条边重新定向,对于无向边 \((u,v)\),只从度数小的连向度数大的边,如果度数相同比编号,显然这样形成一个偏序集,于是每一个三元环 \((i,j,k)\) 与每一个满足 \(i\to j,j\to k,i\to k\) 的边都存在的三元组 \((i,j,k)\) 形成双射,我们就非常暴力地枚举 \(i\),再枚举它的出边 \(j\),再枚举它的出边 \(k\),并检验 \(i\to k\) 的边是否存在即可。容易证明这样复杂度是 \(m\sqrt m\) 的。

总之这题就是一堆正难则反,思维难度倒不算太大

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=2e5;
int n,m,deg[MAXN+5];u64 a,b,c;
u64 sum(u64 l,u64 r){return (l+r)*(r-l+1)/2ull;}
vector<int> g1[MAXN+5],g2[MAXN+5],g[MAXN+5];
vector<u64> s1[MAXN+5],s2[MAXN+5];
int vis[MAXN+5];
int main(){
	scanf("%d%d%llu%llu%llu",&n,&m,&a,&b,&c);
	for(int i=1,u,v;i<=m;i++){
		scanf("%d%d",&u,&v);if(u>v) u^=v^=u^=v;
		g1[v].pb(u);g2[u].pb(v);deg[u]++;deg[v]++;
	}
	u64 ans=0;
	for(int i=0;i<n;i++){
		sort(g1[i].begin(),g1[i].end());
		s1[i].resize(g1[i].size());
		for(int j=0;j<g1[i].size();j++){
			if(j==0) s1[i][j]=g1[i][j];
			else s1[i][j]=s1[i][j-1]+g1[i][j];
		}
		sort(g2[i].begin(),g2[i].end());
		s2[i].resize(g2[i].size());
		for(int j=0;j<g2[i].size();j++){
			if(j==0) s2[i][j]=g2[i][j];
			else s2[i][j]=s2[i][j-1]+g2[i][j];
		}
	}
	for(int i=0;i<n;i++){
		u64 cnt1=i-g1[i].size(),cnt2=n-i-1-g2[i].size();
		u64 sum1=sum(0,i-1)-((g1[i].empty())?0:s1[i].back());
		u64 sum2=sum(i+1,n-1)-((g2[i].empty())?0:s2[i].back());
		ans+=cnt1*cnt2*i*b+cnt1*sum2*c+cnt2*sum1*a;
	}
	for(int i=0;i<n;i++) for(int j:g2[i]){
		int pos1=lower_bound(g1[j].begin(),g1[j].end(),i)-g1[j].begin();
		int pos2=lower_bound(g2[i].begin(),g2[i].end(),j)-g2[i].begin();
		u64 cnt=g1[j].size()-1-pos1+pos2;
		u64 rem=j-i-1-cnt;
		u64 s=sum(i+1,j-1)-s1[j].back()+s1[j][pos1]-((!pos2)?0:s2[i][pos2-1]);
//		printf("%d %d %d %d\n",i,j,rem,s);
		ans-=s*b+rem*i*a+rem*j*c;
	}
	for(int i=0;i<n;i++) for(int j=0;j<g1[i].size();j++){
		int x=g1[i][j];
		if(deg[i]<deg[x]) g[i].pb(x);
		else g[x].pb(i);
	} memset(vis,-1,sizeof(vis));
	for(int i=0;i<n;i++){
		for(int j:g[i]) vis[j]=i;
		for(int j:g[i]) for(int k:g[j]){
			if(vis[k]==i){
//				printf("%d %d %d\n",i,j,k);
				u64 tmp[3]={0};tmp[0]=i;tmp[1]=j;tmp[2]=k;
				sort(tmp,tmp+3);ans-=tmp[0]*a+tmp[1]*b+tmp[2]*c;
			}
		}
	}
	printf("%llu\n",ans);
	return 0;
}
posted @ 2021-03-25 19:52  tzc_wk  阅读(142)  评论(0)    收藏  举报