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;
}