目录
B:Black and white(最小生成树,思维)
题意:
地图规模为5000*5000级别,点权wi(0≤wi<10^5)依靠公式生成,你可以花费点权那么多钱涂黑一个点,如果长方形中3个顶点被涂黑了,那么剩下的顶点可以免费涂黑,询问将整张图涂黑的最小代价是多少?
思路:
考虑将点化为边,边化为点。对于每一行每一列当成一个点,比如点(i,j)等价于第i行向第j列连一条边。
每个点就相当于连接某一行和某一列,连了三个顶点,就相当于某两行某两列在一个集合里,第四个点就不用花费了。问题就变成了连接所有行和列的最小花费,用最小生成树解决。
#include <bits/stdc++.h>
typedef long long ll;
#define int long long
using namespace std;
const double eps = 1e-9;
const int N = 1e5 + 9;
vector<pair<int, int>> v[N];
int fa[5010 * 2];
int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
signed main()
{
int n, m, a, b, c, d, p;
cin >> n >> m >> a >> b >> c >> d >> p;
int pre = a;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
int now = (pre * pre * b + pre * c + d) % p; //代价
pre = now;
v[now].push_back({i, n + j}); //连接行和列
}
}
for (int i = 1; i <= n + m; i++)
{
fa[i] = i;
}
int ans = 0;
int siz = 0;
for(int i=0;i<p;i++)
{
for(auto pii:v[i])
{
int a=pii.first,b=pii.second;
a=find(a);
b=find(b);
if(a!=b)
{
fa[a]=b;
ans+=i;
siz++;
if(siz==n+m-1)
{
cout<<ans;
return 0;
}
}
}
}
}
//当点 ( i , j ) 被染黑后,第 i 行和第 j 列就是连通的了
//问题就转化为连通所有行和列,所以至少是选择n+m-1个点
C:Minimum grid(二分图最大匹配)
题意:
给定一个n×n 的矩阵,有些地方能填数而有些地方不能。然后给出每一行每一列的最大值,问最小的填数总和。
思路:
和上题类似,也要把边转换为点,点转换为边。
只有填数的格子,才能视为行向列连边。
如何求出最小填数总和,只有某个点该行该列的最大值相同,我们就可以省一个点,从而减少总值,如果该行该列的最大值不同,我们无论如何也不会省一个点减少总值,因为如果只作为该行的最大值仍需要一个最大值来填充该列的最大值。省下来的点我就可以少贡献一个最大值
那么我们就需要求出省尽可能多的点,从而减少总值.就需要用到匈牙利算法求最大匹配了。
#include <bits/stdc++.h>
typedef long long ll;
// #define int long long
using namespace std;
const double eps = 1e-9;
const int N = 4e3+10;
int n,m,k;
bool res[N][N];
int match[N];
vector<int>edge[N];
int b[N],c[N];
bool st[N];
bool find(int x)
{
for(auto j:edge[x])
{
if(!st[j])
{
st[j]=true;
if(match[j]==0||find(match[j]))
{
match[j]=x;
//match[x]=j
return true;
}
}
}
return false;
}
signed main()
{
cin>>n>>m>>k;
ll ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
ans+=b[i];
}
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
ans+=c[i];
}
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
res[a][b]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(b[i]==c[j]&&res[i][j])
edge[i].push_back(j+n);
}
}
for(int i=1;i<=n;i++)
{
memset(st,false,sizeof st);
if(find(i))
ans-=b[i];
}
cout<<ans;
}
F:24dian(大模拟)
题意:
给定目标值 mm 和牌数 nn(n≤4),要求用 1−13 点的 n 张牌组成目标值,要求计算过程中每步都有非整数,且不能存在不用分数就实现的情况。按字典序输出全部的答案。
思路:
n很小可以发现n<=3情况下答案是不存在的,考虑n==4即可。
接下来就是大模拟了。。暴力求出所有可能情况以及是否出现小数,统计答案。
#include <bits/stdc++.h>
typedef long long ll;
// #define int long long
using namespace std;
const double eps = 1e-9;
const int N = 1e5 + 9;
int n, m;
vector<double> v, e[N];
int flag, cnt, ans;
bool jc(double x,double y)//判断是否出现小数
{
if(x>(int)(x+eps))
return 1;
if(y>(int)(y+eps))
return 1;
if(x/y>(int)(x/y+eps))
return 1;
return 0;
}
void dfs(int pos, int suc, vector<double> v) //当前位置,是否有小数
{
if (pos == n)
{
if (fabs(v[0] - m) < eps) //判断是否相等
{
flag++;
if (suc)
cnt++;
}
return;
}
int len = v.size();
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len; j++)
{
if (i == j)
continue;
vector<double> t;
t.clear();
for (int k = 0; k < len; k++)
{
if (k != i && k != j)
t.push_back(v[k]);
}
t.push_back(v[i] + v[j]);
dfs(pos + 1, suc, t);
t.pop_back();
t.push_back(v[i] - v[j]);
dfs(pos + 1, suc, t);
t.pop_back();
t.push_back(v[i] * v[j]);
dfs(pos + 1, suc, t);
t.pop_back();
//以上运算不会出现小数
t.push_back(v[i] / v[j]);
dfs(pos + 1, suc|jc(v[i],v[j]), t);
t.pop_back();
}
}
}
bool check(vector<double> v)
{
flag = 0, cnt = 0; // 全部符合要求的个数 符合有小数的个数
dfs(1, 0, v);
if (flag == cnt && cnt)
return 1;
return 0;
}
void find(int w, int z) //第几位 值
{
if (w == n + 1)
{
if (check(v))//判断枚举的数列是否满足
e[ans++] = v;
return;
}
for (int i = z; i <= 13; i++)
{
v.push_back(i);
find(w + 1, i);
v.pop_back();
}
}
int main()
{
scanf("%d%d", &n, &m);
find(1, 1);
printf("%d\n", ans);
for (int i = 0; i < ans; i++)
{
for (int j = 0; j < e[i].size(); j++)
cout << e[i][j] << " ";
puts("");
}
}
J:Counting Triangles(思维,容斥)
题意:
题目生成一个完全图,图上边权只有黑白两种,询问有多少个三角形边的颜色完全一致。
思路:
正着求不好求,正难则反,反着求。
求有多少颜色不相同的三角形的边,再有总数减去即可。
#include <bits/stdc++.h>
typedef long long ll;
// #define int long long
using namespace std;
const int N = 6e6 + 10;
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];
ll res1[N], res2[N];
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();
if(edge[j][i])
res1[j]++,res1[i]++;
else
res2[j]++,res2[i]++;
}
}
ll ans=(ll)n*(n-1)*(n-2)/6;
ll res=0;
for(int i=0;i<n;i++)
{
res+=res1[i]*res2[i];
}
ans-=res/2;
cout<<ans;
return 0;
}