传送门

知识点:并查集+组合数学

并查集合并操作可以理解为使得两个集合的人互相成为朋友,也就是两个集合并在了一起,答案是要求从所有人中挑出四个互相不是朋友的四个人,比较基础的组合数学知识,但因为每个集合的大小预先不知,所以变得难以计算。

假设我们现在算出了合并前的答案,在合并x和y时,设 \(sz[x]\) 为\(x\)所在集合的集合大小,\(sz[y]\) 同理。考虑这两个集合对答案的贡献。有三种情况:

  1. 从x所在集合中取一个人,然后再从其他非y集合中挑选出三个互不在同一集合的人
  2. 从y所在集合中取一个人,然后再从其他非x集合中挑选出三个互不在同一集合的人
  3. 从x,y所在集合中各取一个人,然后再从其他集合中挑选出两个互不在同一集合的人

考虑合并之后

可以发现合并之后x和y在同一集合,仔细观察上面说到的情况1、2,它们对答案的贡献并没有因为合并操作而改变。只有情况3,在合并之后,该贡献被消灭,所以要用上一次的答案减去这个情况,就是合并之后的答案。

该怎么计算?情况3的答案等同于从非x,y的集合中挑两个集合并从这两个集合中各选一个人的情况总数。

举个例子,比如除去x,y所在的集合,剩下的集合的个数分别是

3 4 3 2

那么应该这么计算:

\((3*4 + 3*3 + 3 * 2) + (4*3+4*2) + (3*2)\)

等同于

\[((3+4+3+2) * (3+4+3+2) - (3^2 + 4^2+3^2+2^2)) \over 2
\]

总人数为n,sum为每个集合大小的平方和,那么情况3的总数为

\[num = {(n-sz[x]-sz[y])^2-(sum-sz[x]^2-sz[y]^2)\over 2}
\]

然后 res -= num 更新答案即可

最后更新一下sum和 sz[x]sz[y](取决于用哪个作为所在集合代表元素)

res初始化为\(C_n^4\)

由于n 是1e5,所以对n分情况讨论求\(C_n^4\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
ll n,m;
ll fa[N],sz[N];
int find(int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)fa[i] = i,sz[i] = 1ll;
if(n < 4){
printf("0\n");
while(m--){
int x,y;scanf("%d%d",&x,&y);
printf("0\n");
}
return 0;
}
ll res = 0;
if(n % 4 == 0){
res = n / 4 * (n-1) / 3 * (n-2) / 2 * (n-3);
}
else if(n % 4 == 1){
res = n * (n-1) / 4 * (n-2) / 3 * (n-3) / 2;
}
else if(n % 4 == 2){
res = n / 2 * (n-1) * (n-2) / 4 * (n-3) / 3;
}
else{
res = n / 3 * (n-1)/2 * (n-2) / 1 * (n-3)/4;
}
printf("%lld\n",res);
ll sum = n;
while(m--){
int x,y;
scanf("%d%d",&x,&y);
x = find(x);
y = find(y);
if(x == y){//如果已经在同一集合就不更新答案
printf("%lld\n",res);
continue;
}
sum -= sz[x] * sz[x];
sum -= sz[y] * sz[y];
res -= (sz[x] * sz[y]) * (((n - sz[x] - sz[y]) * (n - sz[x] - sz[y]) - sum)/2);
fa[x] = y;
sz[y] += sz[x];
sum += sz[y] * sz[y];
if(res < 0)res = 0;//res不能减成负数
printf("%lld\n",res);
}
return 0;
}

最新文章

  1. 读书笔记:《HTML5开发手册》--HTML5新的结构元素
  2. Linux LVM学习总结&mdash;&mdash;放大LV容量
  3. JupyterNotebook如何添加table of content
  4. Alamofire 的使用
  5. z-index深入理解
  6. Git进行代码管理的心得
  7. 烂泥:KVM、kickstart与FTP集成
  8. RPi 2B USB 远程桌面
  9. careercup-C和C++ 13.7
  10. linux多线程socket编程一些心得
  11. urllib模块 | Python 2.7.11
  12. CSS3学习系列之选择器(二)
  13. SQL 中的日期和时间类型
  14. HTL里面使用sling model的时候传参问题
  15. mysql---SQLZOO:从WORLD选择教程/ zh
  16. 解决mysql1336
  17. 解决Ubuntu的root账号无法登录SSH问题-Permission denied, please try again.
  18. CentOS7(linux) 通过服务名查询安装目录
  19. 20135337朱荟潼 Linux第六周学习总结——进程的描述和进程的创建
  20. Linux命令学习chroot和chmode

热门文章

  1. nginx基于uwsgi部署Django
  2. ios swift多线程的实现 Multithreading
  3. 关于 mybatis 报invalid comparison: java.util.Arrays$ArrayList and java.lang.String异常
  4. Angular工作笔记(2018/8/8)
  5. vue2.0 与 vue3.0 配置的区别
  6. Windows(Server)环境安装Web服务器(Apache,PHP,Mysql)图文教程
  7. Java抽象类、接口、内部类
  8. Weed:线段树
  9. NOIP模拟 29
  10. ITester软件测试小栈,快来点击领取你的能量值!