2021牛客多校训练营3-J题-Counting Triangles

说实话,这场质量有点高.先上题目:


链接:https://ac.nowcoder.com/acm/contest/11254/J
来源:牛客网
 

题目描述

Goodeat finds an undirected complete graph with n vertices. Each edge of the graph is painted black or white. He wants you to help him find the number of triangles (a, b, c) (a < b < c), such that the edges between (a, b), (b, c), (c, a) have the same color. To avoid the input scale being too large, we use the following code to generate edges in the graph.

namespace GenHelper
{
    unsigned z1,z2,z3,z4,b,u;
    unsigned get()
    {
        b=((z1<<6)^z1)>>13;
        z1=((z1&4294967294U)<<18)^b;
        b=((z2<<2)^z2)>>27;
        z2=((z2&4294967288U)<<2)^b;
        b=((z3<<13)^z3)>>21;
        z3=((z3&4294967280U)<<7)^b;
        b=((z4<<3)^z4)>>12;
        z4=((z4&4294967168U)<<13)^b;
        return (z1^z2^z3^z4);
    }
    bool read() {
      while (!u) u = get();
      bool res = u & 1;
      u >>= 1; return res;
    }
    void srand(int x)
    {
        z1=x;
        z2=(~x)^0x233333333U;
        z3=x^0x1234598766U;
        z4=(~x)+51;
      	u = 0;
    }
}
using namespace GenHelper;
bool edge[8005][8005];
int main() {
  int n, seed;
  cin >> n >> seed;
  srand(seed);
  for (int i = 0; i < n; i++)
    	for (int j = i + 1; j < n; j++)
        	edge[j][i] = edge[i][j] = read();
 	return 0;
}


The edge array in the above code stores the color of the edges in the graph. edge[i][j]=1 means that the edge from i to j is black, otherwise it is white (∀0≤i≠j≤n−1\forall 0 \le i \neq j \le n-1∀0≤i​=j≤n−1).

Ensure that there is an approach that does not depend on the way the data is generated.
 

输入描述:

 

The first line contains two integers n(n≤8000),seed(seed≤109)n(n \le 8000), seed (seed \le 10^9)n(n≤8000),seed(seed≤109), denote the number of vertices and the seed of random generator. 

输出描述:

Output a line denoting the answer. 

示例1

输入

10 114514

输出

35

说明

There're 35 triangles that all three edges have the same color. 

题目大意是有一个无向图完全图,用题目所给的这段代码去生成一个二维数组(只含有0,1),二维函数的两个下标含义是指两个任意的点,里面存的就是这两个点之间边的颜色,为0就是白,1就是黑色.

本题采用的是容斥的思想,要求三条边颜色相等的三角形数量,就会非常难算,可是如果换一种思路,我们把所有杂色(边颜色不完全相同)三角形数求出来,用所有可以组成三角形情况来减去所有杂色三角形数目,即为题目所求.

我们可以知道三角形组成的情况只有两种,纯色(三条边颜色都相同),杂色(三条边两边颜色相同,另一条不同).而确定一个杂色三角形,我们只需要选两条边,保证两条边颜色不同,那么无论下一根怎么选,它组成的一定是一个杂色三角形.那么我们把所有这种选两条边情况算出来,即为杂色三角形个数.

此时,我们只需要遍历一次这个二维数组,将所有边组合情况进行判断储存,你可以选择计算全为黑色或者全为白色边的个数,因为这些边非黑即白,算一条另一条也很好算,总数减去一种的数目即可,用黑色边数乘以白色边数,就是这些黑色边和白色边的组合数,即为杂色三角形的个数,当然要注意,以为当我们计算1,2的边和2,1的边会重复,所以要把这个的数目除以二,再用总数减去杂色三角形个数,及可求出结果.

另外所有三角形的个数就是c(3,n),从n里面选三个的数量.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
bool edge[8005][8005];
unsigned z1,z2,z3,z4,b,u;
    unsigned get()
    {
        b=((z1<<6)^z1)>>13;
        z1=((z1&4294967294U)<<18)^b;
        b=((z2<<2)^z2)>>27;
        z2=((z2&4294967288U)<<2)^b;
        b=((z3<<13)^z3)>>21;
        z3=((z3&4294967280U)<<7)^b;
        b=((z4<<3)^z4)>>12;
        z4=((z4&4294967168U)<<13)^b;
        return (z1^z2^z3^z4);
    }
    bool read() {
      while (!u) u = get();
      bool res = u & 1;
      u >>= 1; return res;
    }
    void srand(int x)
    {
        z1=x;
        z2=(~x)^0x233333333U;
        z3=x^0x1234598766U;
        z4=(~x)+51;
        u = 0;
    }
vector<int>g[8010];
int main() {
  ll n, seed;
  cin >>n>> seed;
  srand((int)seed);
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++){
            edge[j][i] = edge[i][j] = read();
            if(edge[i][j]==0){
                g[i].push_back(j);
                g[j].push_back(i);
            }
    }
    ll ans=0;
    for (int i=0;i<n;i++)
        ans=ans+(n - 1 - g[i].size())*g[i].size();
    ans/=2;
    cout<<(n*(n - 1)*(n - 2))/6-ans<< endl;
    return 0;
}

这里遍历入队的过程,就是以1,2,3...n为出发点,分别与其他点进行匹配,确定它们匹配的边是否为白,再在下面遍历这个邻接表,计算分别以每个点为出发点的情况下,有多少种杂色匹配情况.ans就要每次加上以这个点为端点的边所组成的杂色情况(白色边数乘以黑色边数),最后结果会有重复,因为以任一点为一方的端点,下一次一定会被其他点用于另外一方的端点,所以除二.总数减去该数即为所求.

这题不难,首先就是不要被那么长的题目迷惑,随机生成那段直接抄就行,然后就是思考.其实最重要就是把题目看懂,不是特别难想(虽然我也是学长教的,但是听了感觉不难写),大家好好学英语吧,题目读懂很重要.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值