2021牛客暑期多校训练营3

2021牛客暑期多校训练营3

B Black and white

题目:
在两行两列的四个交点上,我们可以给三个格点涂成黑色,剩下的格点可以自动被涂成黑色。
问我们将所有方格全部变成黑色,最少需要多少的花费。

解决:
我们通过上面题目中的信息进行挖掘,我们知道两行两列四个交点中的三个交点,就可以自动帮我们无花费的涂掉第四个格子。
假设这四个点为(x1,y1),(x1,y2),(x2,y1),(x2,y2)。我们知道了三个点,一定可以知道第四个点,我们可以通过前三个坐标的顺延到第四个坐标。这是因为,通过三个点,我们就可以得到了[x1,x2,y1,y2]那么,第四个点不就是很好求了么。
那么,我们最少需要得到多少格点,可以得到任意一个点的坐标呢,答案是n+m-1,可以得到[x1,x2,…,xn,y1,y2,…,ym]就可以任意一个坐标了。
所以,这就变成了一个n+m-1大小的最小生成树问题了

#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
int n, m, a, b, c, d, p;
int fa[N << 1];
vector<pair<int, int> > v[100005];
int find(int x)
{
  return fa[x] == x ? x : fa[x] = find(fa[x]);
}
bool merge(int a, int b)
{
  a = find(a), b = find(b);
  if (a == b) return false;
  fa[a] = b;
  return true;
}
int w[N * N];
int main()
{
  cin >> n >> m >> a >> b >> c >> d >> p;
  for (int i = 1; i <= n + m; i ++ ) fa[i] = i; 
  w[0] = a;
  for (int i = 0; i <= n * m; i ++ )
    w[i + 1] = (1ll * w[i] * w[i] * b + 1ll * w[i] * c + d) % p;
  for (int i = 1; i <= n; i ++ )
    for (int j = 1; j <= m; j ++ )
      v[w[m * (i - 1) + j]].push_back({i, j});
  long long ans = 0;
  for (int i = 0; i < p; i ++ )
    for (auto it : v[i])
    {
      int x = it.first, y = it.second;
      if (merge(x, y + n)) ans += i;
    }
  cout << ans << endl;
  return 0;
}

C Minimum grid

题目:
给定m个点,可以填写数值,
给定n个a,表示每一行的最大值,给定n个b表示每一列中的最大值。
问,在满足最大行和列的条件下,总和最小为多少。

解决:
最坏情况,满足a和b情况下,填满所有的数字。
如果存在某行某列下存在格点,且这个这一行和这一列的最大值相同,那么就可以减少一个。
我们要做的就是尽可能地成全这种匹配。
那么,我们就是尽可能地给每个最大值找到尽可能做的匹配,这个匹配,左部是横坐标,右部是纵坐标,而是否可以连线,表示他们是否这行和这列存在相同地最大值。让每个权值做到可能地匹配。同时注意,不同地权值中间是不存在边地。
所以,二分部走一遍匈牙利算法,得到最大匹配。

#include <bits/stdc++.h>
using
 namespace std;
const int N = 2005;
int n, m, k;
int a[N], b[N];
vector<int> g[N];
int match[N];
bool st[N];
long long ans;
bool dfs(int x)
{
  for (auto y : g[x])
  {
    if (st[y]) continue;
    st[y] = true;
    if (!match[y] || dfs(match[y]))
    {
      match[y] = x;
      return 1;
    }
  }
  return 0;
}
int main()
{
  scanf("%d%d%d", &n, &m, &k);
  for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
  for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);
  while (m -- )
  {
    int x, y; scanf("%d%d", &x, &y);
    if (a[x] == b[y]) //说明存在一个位置,让x行和y列有共同的最大值
      g[x].push_back(y);
    //但是,我们要做的是找到这样的最大匹配
    //我们这里的匹配,是建立在此行和此列的最大值相同的情况下的,所有,我们尽管可能有多组匹配
    //但是,他们匹配的权值都是相同的,不管怎样匹配,只要两者能匹配,都是相同最大值情况下的匹配
    //不会出现大的匹配在匹配过程中变成小的匹配的。
  }
  for (int i = 1; i <= n; i ++ )
  {
    memset(st, 0, sizeof st);
    dfs(i);
  }
  for (int i = 1; i <= n; i++ ) ans += a[i] + b[i];
  for (int i = 1; i <= n; i ++ )
    if (match[i]) ans -= b[i];
  cout << ans << endl;
  return 0;
}

Counting Triangles

题目:给定一个无向完全图,用二维邻接矩阵存储,任意两点之间边用权值0或1表示。0和1只能内部联通。现在问,有多少个连通地三角形。

解决:使用到了容斥原理。
对于任意地非法三角形,一定是2个同色边加上1个其他颜色地边。所以,对于每个三角形来说,一定存在两个点,是连这两种颜色地边,另一个连接着两个同色的边。我们统计第一种点的数量然后总方案-非法方案/2

#include <bits/stdc++.h>
using namespace std;

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();
  long long ans = 1ll * n * (n - 1) * (n - 2) / 6;
  long long x, y, cnt = 0;
  for (int i = 0; i < n; i ++ ) 
  {
    x = 0, y  = 0;
    for (int j = 0; j < n; j ++ )
    {
      if (i == j) continue;
      if (edge[i][j]) x ++;
      else y ++;
    }
    cnt += x * y;
  }
  cout << ans - cnt / 2 << endl;
 	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值