中国石油大学(北京)第三届“骏码杯”程序设计竞赛(同步赛)
E-Construction Complete!
题意:
有块 的区域,问其中可以建多少个 的全是 的矩阵,并且满足该矩阵到某一 的曼哈顿距离小于等于 。
题解:
先用多元bfs跑一遍各个点到最近 的最短距离,用二维前缀和判断最短距离小于等于 的 区域是否全是 。
这题就因为最开始 dist 初始为 -1 ,漏想了当没有 时的情况,一直 wa ,调了一整天。
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#define int long long
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const int N = 1000010, M = 50010, P = 1e12;
const int Mod = 1e9 + 7;
const int INF = 1e18;
int n, T, m, k;
int r, s, d;
int res;
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
void bfs(vector<vector<int> >& dist, vector<string>& g)
{
queue<PII> q;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
if (g[i][j] == 'x')
{
dist[i][j] = 0;
q.push({i, j});
}
while (q.size())
{
PII t = q.front();
q.pop();
int x = t.first, y = t.second;
for (int i = 0; i < 4; i ++ )
{
int xx = x + dx[i], yy = y + dy[i];
if (xx < 1 || xx > n || yy < 1 || yy > m) continue;
if (dist[xx][yy] != n + m) continue;
dist[xx][yy] = dist[x][y] + 1;
q.push({xx, yy});
}
}
}
void solve()
{
cin >> n >> m >> r >> s >> d;
res = 0;
vector<vector<int> > c(n + 5, vector<int>(m + 5, 0));
vector<vector<int> > dist(n + 5, vector<int>(m + 5, n + m));
vector<vector<int> > isvaild(n + 5, vector<int>(m + 5, 0));
vector<string> g(n + 5);
for (int i = 1; i <= n; i ++ )
{
g[i].resize(m + 5);
for (int j = 1; j <= m; j ++ )
cin >> g[i][j];
}
bfs(dist, g);
// for (int i = 1; i <= n; i ++ )
// for (int j = 1; j <= m; j ++ )
// cout << dist[i][j] << ' ';
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
{
c[i][j] = (g[i][j] == '.' ? 0 : 1);
c[i][j] += c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1];
isvaild[i][j] = (dist[i][j] <= d ? 1 : 0);
isvaild[i][j] += isvaild[i - 1][j] + isvaild[i][j - 1] - isvaild[i - 1][j - 1];
}
for (int i = 1; i + r - 1 <= n; i ++ )
for (int j = 1; j + s - 1 <= m; j ++ )
{
int t = c[i + r - 1][j + s - 1] - c[i - 1][j + s - 1] - c[i + r - 1][j - 1] + c[i - 1][j - 1];
int ismatch = isvaild[i + r - 1][j + s - 1] - isvaild[i - 1][j + s - 1] - isvaild[i + r - 1][j - 1] + isvaild[i - 1][j - 1];
if (!t && ismatch)
res ++ ;
}
cout << res << endl;
}
signed main()
{
quick_cin();
cin >> T;
//getchar();
//T = 1;
while (T -- )
{
solve();
}
return 0;
}
F-最小异或对
题意:
实现对于集合的增加、删除、查询集合中的最小异或对的值三个操作。
题解:
前置芝士引入, 个数的最小异或对就是 个数排序后的相邻异或和的最小值。
证明:
我们以三个数 为例,假设 。
那么思考对于 和 二进制从左到右第一个不同的位第 位,此时 。
1.假设 ,则 ;
2.假设 ,则 ;
得证。
已知上面的结论,那么本题就清晰了很多,创建两个 multiset 分别存当前的所有数和当前所有数排序后的相邻异或和。
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#define int long long
#define PI acos(-1)
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const int N = 2000010, M = 50000010, P = 1e12;
const int Mod = 1e9 + 7;
const int INF = 1e18;
int n, T, m, k;
int r, s, d;
int res = INF;
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
multiset<int> tmp, ans;
void add(int x)
{
tmp.insert(x);
auto it = tmp.find(x);
//倘若x不是最小,计算出x和左相邻的数的异或和
if (it != tmp.begin()) ans.insert(*prev(it) ^ x);
//倘若x不是最大,计算出x和右相邻的数的异或和
if (next(it) != tmp.end()) ans.insert(*next(it) ^ x);
//倘若x既不是最大又不是最小,把先前x的左右相邻的数的异或和删去
if (it != tmp.begin() && next(it) != tmp.end())
ans.erase(ans.find(*prev(it) ^ *next(it)));
}
void del(int x)
{
auto it = tmp.find(x);
//倘若x不是最小,删去x和左相邻的数的异或和
if (it != tmp.begin()) ans.erase(ans.find(*prev(it) ^ x));
//倘若x不是最大,删去x和右相邻的数的异或和
if (next(it) != tmp.end()) ans.erase(ans.find(*next(it) ^ x));
//倘若x既不是最大又不是最小,把先前x的左右相邻的数的异或和还原
if (it != tmp.begin() && next(it) != tmp.end())
ans.insert(*prev(it) ^ *next(it));
tmp.erase(tmp.find(x));
}
void solve()
{
string op;
cin >> n;
while (n -- )
{
cin >> op;
if (op == "ADD")
{
int x;
cin >> x;
add(x);
}
else if (op == "DEL")
{
int x;
cin >> x;
del(x);
}
else
{
cout << *ans.begin() << endl;
}
}
}
signed main()
{
quick_cin();
// cin >> T;
// getchar();
T = 1;
while (T -- )
{
solve();
}
return 0;
}
I-最大公约数求和
题意:计算 ,其中 , 表示 和 的最大公约数。
题解:
前置芝士:
幂数取模:。
使用费马小定理证明:
原式
由费马小定理得 ,所以 。
于是得到最后式子: ,得证。
使用上述公式,我们通过遍历将 和 的最大公约数筛出来。
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#define int long long
#define PI acos(-1)
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const int N = 20000010, M = 100010, P = 1e12;
const int mod = 1e9 + 7;
const int INF = 1e18;
int n, T, m, k;
int r, s, d;
int res = 0;
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
int g[N];
int f[N];
int primes[N], idx;
bool st[N];
int gcd(int a, int b)
{
return b ? gcd(b, a % b): a;
}
int qmi(int a, int b, int p)
{
int res = 1;
while (b)
{
if (b & 1) res = (res * a) % p;
a = (a * a) % p;
b >>= 1;
}
return res;
}
void solve()
{
cin >> n >> k;
if (!k || k == 1)
{
res = (1 + n) * n / 2 % mod;
cout << res << endl;
return;
}
res = 1;
g[1] = f[1] = 1;
for (int i = 2; i <= n; i ++ )
{
if (!st[i])
{
primes[ ++ idx ] = i;
f[i] = qmi(i, k % (mod - 1), mod);
g[i] = gcd(i, k);
}
for (int j = 1; j <= idx && primes[j] * i <= n; j ++ )
{
st[i * primes[j]] = true;
f[i * primes[j]] = f[i] * f[primes[j]] % mod;
if (i % primes[j] == 0)
{
g[i * primes[j]] = g[i];
//特判gcd
if (k % (g[i] * primes[j]) == 0) g[i * primes[j]] *= primes[j];
break;
}
g[i * primes[j]] = g[i] * g[primes[j]];
}
res = (res + g[i] * f[i]) % mod;
}
cout << res << endl;
}
signed main()
{
quick_cin();
// cin >> T;
// getchar();
T = 1;
while (T -- )
{
solve();
}
return 0;
}