比赛链接:Here
1001 - Guess and lies
1002 - Black and white (Kruskal & 并查集)
为了实现最少花费,需满足:在我们涂完若干个点后,其他的点对总花费不再有贡献(涂黑这些点时不花钱了)
我们会发现 :最少,我们需要涂黑 n + m - 1
个点,且涂完这些点后 所有的行 和所有的列 会都在一个联通块里面
举例 如下:
不妨令 n = 2,m = 2,此时我们最少需要涂 3个点
令 (g[1][1] = g[1][2] = g[2][1] = 1,g[2][2] = 3)
为了最少花费,我们要涂的三个点显然是:(1,1),(1,2),(2,1)
涂完(1,1)后,我们把 行1 和 列1 放到一个联通块里面
涂完(1,2)后,我们把 行1 和 列2 放到一个联通块里面
涂完(2,1)后,我们把 行2 和 列1 放到一个联通块里面
到此,所有的行 和 所有的列 都在一个联通块里了,同时我们也实现了最少花费
在这个例子中,涂黑任意三个点都可以实现 把所有的行 和 所有的列 都放在一个联通块里
但为了 最少花费,所以我们选择的是 ((1,1),(1,2),(2,1)) 这三个点
我们提炼出两个关键字 联通块、最小花费:连通块(并查集)+ 最小花费 = Kruskal(最小生成树)
然后发现直接根据条件写一个并查集优化更好一点
const int N = 1e5 + 10;
int f[N];
vector<pair<int, int>> v[N];
int find(int x) {return f[x] == x ? x : f[x] = find(f[x]);}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
ll a, b, c, d, p, ans = 0;
cin >> n >> m >> a >> b >> c >> d >> p;
for (int i = 1; i <= n + m; ++i) f[i] = i;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
a = (a * a * b + a * c + d) % p;
v[a].push_back(make_pair(i, j + n));
}
for (int i = 0; i <= p; ++i)
for (auto [x, y] : v[i]) {
int xx = find(x);
int yy = find(y);
if (xx != yy) {
ans += i;
f[xx] = yy;
}
}
cout << ans;
}
1003 - Minimum grid (二分图)
给出 (n*n) 大小的网格,每一个最大值 (b_i) ,每一列最大值 (c_i) ,你需要对一些点赋值,使得总的赋值和最小
将每个数值对应的行,以及每个数值对应的列分别进行存储。枚举每一个数值,范围 ([1,k])
枚举当前数值所对应的行和列,如果当前行列可以填数,则将行和列建边,求出行和列的最大匹配数。
当前数值的贡献为:(当前值对应的行数+当前值对应列数-最大匹配数)*当前值。
使用 vector
进行存储,所对应的值就是:
此处的最大匹配值可以理解为当前行和列的最大值均已经满足,可以填 0
的最大数量。
const int N = 1e4 + 10, M = 1e6 + 10, inf = 1e8;
int n, m, k, x;
int mp[N][N];
vector<int>G1[M], G2[M];
vector<int>G[N];
int st[N], match[N];
bool dfs(int u) {
for (auto v : G[u]) {
if (st[v]) continue;
st[v] = 1;
if (match[v] == -1 || dfs(match[v])) {
match[v] = u;
return 1;
}
}
return 0;
}
int cal() {
memset(match, -1, sizeof(match));
int res = 0;
for (int i = 1; i <= n + n; ++i) {
memset(st, 0, sizeof(st));
if (dfs(i))res++;
}
return res;
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> m >> k;
for (int i = 1; i <= n; ++i) cin >> x, G1[x].push_back(i);
for (int i = 1; i <= n; ++i) cin >> x, G2[x].push_back(i + n);
while (m--) {
int x, y;
cin >> x >> y;
mp[x][y] = 1;
}
ll ans = 0 ;
for (int now = k; now >= 1; --now) {
if (G1[now].size() == 0 && G2[now].size() == 0) continue;
for (int i = 1; i <= n + n; i++) G[i].clear();
for (auto i : G1[now])
for (auto j : G2[now])
if (mp[i][j - n]) G[i].push_back(j), G[j].push_back(i);
// 建图
ll maxpipei = cal() / 2;
ll maxm = G1[now].size() + G2[now].size() - maxpipei;
ans = ans + maxm * now;
}
cout << ans << '
';
}
二分图相关题:Here
没看出这道题是二分图太丢脸了(别骂了,别骂了)
1004 - Count
1005 - Math (签到?)
比赛时,我擦这公式咋推的啊,这么多人过的???
赛后很好奇为咩能出现 IMO 的题啊!!https://zhuanlan.zhihu.com/p/32815892
最后靠的打表找规律了... (打表yyds)
经过一系列的复杂计算及推演最终得出一个结论:任何x,y都会满足一个式子:
对任意 (k>=2,a[i] = pow(k,2) * a[i-1] - a[i-2]) ,其中 (a[0]=0,a[1]=k) 。
那么我们只要枚举其中较大的那个数,打表后二分即可,由于(a[2]=k^3),因此我们只需要枚举到 (1e6) ,时间复杂度 (mathcal{O}(1e6 + Tlog n))
const int N = 1e6 + 10;
vector<ll>a;
ll b[N];
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
for (int k = 2; k <= 1e6; ++k) {
b[0] = 0, b[1] = k;
for (int i = 2; i <= 60; ++i) {
b[i] = pow(k, 2) * b[i - 1] - b[i - 2];
if (b[i] > 1e18 || b[i] < 0)break;
a.push_back(b[i]);
}
}
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
int _; for (cin >> _; _--;) {
ll n;
cin >> n;
cout << upper_bound(a.begin(), a.end(), n) - a.begin() + 1 << "
";
}
}
贴一下 nagisa_菜鸡桑的思路和推导过程
这道题一拿到手应该大部分人和我一样想到打表。打出一部分表后,我们发现,任意数 ((x,x^3)) 都符合条件。
接下来,设 (k(xy + 1) = x^2 + y ^2) ,此时固定 (x) 整理方程为 (y^2 - kxy - k + x^2 = 0)
之所以固定 (x) 是为了接下来便于枚举 (x) ,
由韦达定理
(伟大定理):(y_1 + y_2 = kx)
- 我们现在知道 ((x,y)) 符合条件,那么也就说明 ((y,x)) 也成立,那么我们可以通过 ((x,kx - y)) 找出以 (y) 作为第一个元素的数对,即 ((y,一个数))
- 我们现在知道 ((x,y)) 符合条件,因此
一个数
= (ky - x) ,即我们得到的新的数对为 ((y,ky - x))此时再回头看 ((x,x^3)) 代入
((x,x^3)) 此时 (k=x^2)
((x^3,x^5-x))
...
无限套娃下去。(有点类似于gcd)
之后就是把打出来的第二元素放进数组里,排序然后二分就行了。
1006 - 24dian
为咩我都没看出规律
等巨佬给我讲一下思路把
// 待补
1007 - Yu Ling(Ling YueZheng) and Colorful Tree
1008 - Ling Qiu, Luna and Triple Backpack
1009 - Kuriyama Mirai and Exclusive Or (树状数组维护差分)
这里是一个调了超级久的树状数组发现错了的蒟蒻(
菜鸡)正解是用树状数组维护写一个差分数组
const int N = 6e5 + 10;
int n, q, a[N], aa[N];
bool b[N][30];
void update(int l, int x) {
for (int i = 0, j = 1; i < 30; ++i, j <<= 1) {
if (x & j and l <= n) aa[l] ^= j;
int k = (((x >> i) + 1) << i) - x + l;
if (k <= n) b[k][i] ^= 1;
}
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> q;
for (int i = 1; i <= n; ++i )cin >> a[i];
while (q--) {
int op, l, r, x;
cin >> op >> l >> r >> x;
if (op) {
update(l, x);
update(r + 1, x + r - l + 1);
} else
aa[l] ^= x, aa[r + 1] ^= x;
}
for (int i = 0, k = 1; i < 30; i++, k <<= 1) {
for (int j = 1; j <= n; ++j) {
if (j + k <= n)
b[j + k][i] ^= b[j][i];
if (b[j][i])
aa[j] ^= k;
}
}
for (int i = 1; i <= n; ++i) {
aa[i] ^= aa[i - 1];
cout << (a[i] ^ aa[i]) << "
"[i == n];
}
}
1010 - Counting Triangles (签到?)
#include<bits/stdc++.h>
#define LL long long
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(LL x) {
z1 = x;
z2 = (~x) ^ 0x233333333U;
z3 = x ^ 0x1234598766U;
z4 = (~x) + 51;
u = 0;
}
}
using namespace GenHelper;
bool edge[8005][8005];
LL a[8005];
int main() {
LL 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[i][j]) a[i]++, a[j]++;
}
LL ans = 0;
for (int i = 0; i < n; i++)
ans += a[i] * (n - a[i] - 1);
cout << n * (n - 1) * (n - 2) / 6 - ans / 2;
}