UVALive - 6804 (状压+容斥)

本文解析了一道ACM竞赛题目,通过使用bitset求交集的方法,结合容斥原理解决了一个关于点与连线的问题。文章详细介绍了算法思路,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:
https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4816

题意:有若干个点,若干个点之间有直线相连,问全部的点中,有多少三个点之间相互没有直接连接的方案。

分析:这道题很容易想到(怎么可能orz)用bitset求交集,然后用容斥,补这道题的时候容斥部分看了好久,果然我数学还是太菜了。先求出所有可能的排列组合all = C(n,3); 对于容斥,one是那些连有一条边的两个点,m*C(n-2(除去连着的两点再选一点),1); two是那些一个点可以连哪两个点的排列组合,设一个点连接的点数为temp,那么two = C(temp,2); three是三个点相互连接的情况所以就是求两个点连同一个点有多少种情况,即求两个点连接的点的交集。然后ans = all-one+two+three就可以了。
这里写图片描述

one里面包含two和three, two里面包含three什么的xjb容斥;
下面上代码(参考hq):

#include<cstdio>
#include<cstring>
#include<string>
#include<sstream>
#include<iostream>
#include<set>
#include<map>
#include<queue>
#include<deque>
#include<vector>
#include<algorithm>
#include<cctype>
#include<math.h>
#include<stdlib.h>
#include<stack>
#include<ctime>
#include<bitset>
#include<list>
#define mst(a,b) memset(a,b,sizeof(a))
#define lowbit(x) (x)&(-x)
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair<int,int>
#define eps 1e-3
const long long N = 1e12;
const long long mod=(long long)1e9+7;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
const int maxn=5e3+5;
const double g = 10;
using namespace std;

LL n,m;
LL u[20005], v[20005];
bitset<maxn> b[maxn], kk;

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    //ios::sync_with_stdio(false);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T, cas = 0; scanf("%d", &T);
    while(T--){
        scanf("%lld%lld", &n,&m);
        for(int i = 1; i <= n; i++) b[i].reset();
        for(int i = 1; i <= m; i++){
            scanf("%lld%lld", &u[i],&v[i]);
            b[u[i]][v[i]] = 1; b[v[i]][u[i]] = 1;
        }
        LL all = n*(n-1)*(n-2)/6, two = 0, three = 0;
        LL one = m*(n-2);
        for(int i = 1; i <= n; i++){
            LL temp = b[i].count();
            two+=temp*(temp-1)/2;
        }
        for(int i = 1; i <= m; i++){
            kk = b[u[i]]&b[v[i]];
            three+=kk.count();
        }
        three/=3;  //会有重复选到的情况所以/3;
        printf("Case #%d: %lld\n", ++cas, all-one+two-three);
    }
    return 0;
}

ps:蟹蟹各位大佬帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值