牛客2021暑期多校训练营3

原题链接

2021牛客暑期多校训练营3

B.Black and white

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述

Goodeat has a white chessboard with \(n\) rows and \(m\) columns.
Each grid \((i, j)\) has a weight \(c(i, j)\). At any time, the grid \((i, j)\) can be dyed black at the cost of \(c(i, j)\).
Goodeat has a special talent. For the four intersecting squares of any two rows and two columns, if three of them are black squares, Goodeat can dye the fourth square black without any cost.
Please find out the minimum cost of dyeing a black chessboard.
Due to the large number of grids, we use the following method to generate weights:
\(A_0 = a\)
\(A(i+1) = (Ai * Ai * b + Ai * c + d)% p\)
Where \(A(m*(i-1)+j)\) is the cost \(c(i, j)\) of the grid in the \(i-\)th row and \(j-\)th column \((1 \le i \le n, 1\le j\le m)\).

输入描述:

The first line contains seven integers \(n,m,a,b,c,d,p\).\((a,b,c,d <p\le 100000, n,m\le 5000)\)

输出描述:

Output a single integer denoting the answer.

输入

4 4 1 2 3 4 7

输出

20

说明

Here is the weight matrix:

2 4 6 3
3 3 3 3
3 3 3 3
3 3 3 3

解题思路

最小生成树

EOJ Monthly 2021.9——C. Connection类似的思路
开始时都是白棋,每行每列都不连通,目的是使花费最小,倘若一开始便联通,则不用花费,所以目的是使其联通并且代价最小,所以就是求行、列构成点的最小生成树~
注意:本题是稠密图,应使用prim算法

  • 时间复杂度:\(O(n^2)\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int adj[N*2][N*2];
int n,m,a,b,c,d,p;
int dis[N*2];
bool v[N*2];
void prim()
{
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    for(int i=1;i<n+m;i++)
    {
        int x=0;
        for(int j=1;j<=n+m;j++)
            if(!v[j]&&(x==0||dis[j]<dis[x]))x=j;
        v[x]=1;
        for(int y=1;y<=n+m;y++)
            if(!v[y])
                dis[y]=min(dis[y],adj[x][y]);
    }
}
int main()
{
    scanf("%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&p);
    int A=a;
    memset(adj,0x3f,sizeof adj);
    for(int i=1;i<=n+m;i++)adj[i][i]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            A=(1ll*A*A*b%p+1ll*A*c+d)%p;
            adj[i][n+j]=adj[n+j][i]=min(adj[i][n+j],A);
        }
    }
    //prim算法求最小生成树
    prim();
    int res=0;
    for(int i=2;i<=n+m;i++)res+=dis[i];
    printf("%d",res);
    return 0;
}



J.Counting Triangles

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

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 \((\forall 0 \le i \neq j \le 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 \le 8000), seed (seed \le 10^9)\), denote the number of vertices and the seed of random generator.

输出描述:

Output a line denoting the answer.

输入

10 114514

输出

35

说明

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

解题思路

容斥原理

原问题不好直接求解,我们不妨考虑其反面,即:异色三角形
我们只需要找出这样的点的数量:其两边异色,找出所有这样的点的数量 \(cnt\) 后,\(cnt \div 2\) 即异色三角形的个数,用总个数 \(C_n^3\) 减去即可~

\(\color{red} {为什么 cnt \div 2 即异色三角形的个数?}\)
我们求得的 \(cnt\) 是异色角的个数,而一个异色三角形由两个异色角和一个同色角构成,每个点枚举的时候都会重复枚举一次
注意:可能会超int

  • 时间复杂度:\(O(n^2)\)

代码

#include<bits/stdc++.h>
using namespace std;
using LL=long long;
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();
    LL diff=0;
    for(int i=0;i<n;i++)
    {
        LL s[2]={0,0};
        for(int j=0;j<n;j++)
        {
            if(i==j)continue;
            s[edge[i][j]]++;
        }
        diff+=s[0]*s[1]/2;
    }
    cout<<1ll*n*(n-1)*(n-2)/6-diff;
 	return 0;
}

posted @ 2021-09-16 21:08  zyy2001  阅读(47)  评论(0编辑  收藏  举报