题目整理:
第一场因为起晚了没赶上前半场(汗)
我参与做的是H和K
H:Hash Function
题意是给出n个数a1~an,求出一个最小的模数p,使得所有ai对p取模互不相同
1≤n≤500000
0≤a i≤500000
并且ai之间互不相等
标算似乎是FFT还是NTT,不太清楚
我们队的做法是先用0.5s删掉不可能的答案
再用剩下的时间找出可行解
已知余数互不相等,且过大的模数不具备意义,那么n<=p<=a[n]+1
对于序列中随机两个数,a[i]和a[j],如果使得a[i]%p==a[j]%p
则abs(a[i]-a[j])的因数显然可以作为p的取值
那么我们用0.5s的时间,每一次随机选取序列中的两个数,然后做差找出范围内的因数并删掉
然后是暴力查找:
对于给定的一个模数p,在O(n)的时间内判断它是否合题
当给出模数p的时候,我们可以把整个序列扔进桶里:
例如序列:
3
2 5 7
在桶里:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 1 | 0 | 1 |
对于一个模数p,将桶序列分成等长的几段,判断每一段对应位置是否只有一个1
比如说选取模数3
0 | 1 | 2 |
---|---|---|
0 | 0 | 1 |
3 | 4 | 5 |
---|---|---|
0 | 0 | 1 |
6 | 7 | \ |
---|---|---|
0 | 1 | \ |
对应起来,显然在2和5上起了冲突,因此非法
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 500000 + 10;
const int RAND = 100000;
int a[N];
bool b[N];
bool c[N];
void fun(int n) {
for (int i = 1; i <= sqrt(n); ++i) {
if (n % i == 0) {
b[i] = false;
b[n / i] = false;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
sort(a, a + n);
for (int i = n; i <= a[n - 1] + 1; ++i) {
b[i] = true;
}
srand(time(nullptr));
auto t = clock();
for (int i = 0; i < RAND; ++i) {
auto t1 = clock();
if ((t1 - t) / CLOCKS_PER_SEC >= 1)
break;
int l = rand() % n;
int r = rand() % n;
if (l > r)
swap(l, r);
fun(a[r] - a[l]);
}
for (int i = 0; i < n; i++)
c[a[i]] = 1;
for (int i = n; i <= a[n - 1] + 1; ++i) {
if (!b[i])
continue;
int fl = 0;
for (int j = 0; j < i; j++) {
int cnt = 0;
for (int k = 0; k <= 500000 / i && k * i + j <= 500000; k++)
if (c[k * i + j] == 1)
cnt++;
if (cnt > 1) {
fl = 1;
break;
}
}
if (fl == 0) {
cout << i << endl;
break;
}
}
return 0;
}
K:Knowledge Test about Match
题意:给定2个序列
A={0,1,2,···,n-1}
B={b0, b1,b2,···,bn-1}
我们要求对B进行重新排序,将以下函数最小化
已知这个差值函数的最小值可能很难求,只要求最后重新排序后B的序列算出来的差值函数与最小值的误差不超过4%
已知B是随机化生成的
10<=n<=1000
我们队的方法:(又是随机化emmm)
我最初提出来的方法是这样:
首先如果bi能和aj对应上,那就优先一一对应,比如说B中有一个0,那么它优先填到第一个位置上
剩下的序列进行随机排序,然后算一下差值函数取一个最小的
因为是瞎想的算法,被hack掉了,队友说不管怎么随机排序,算出来的差值函数还不如直接从小到大排序来的小(哭唧唧)
队友又改了一下随机化算法,过了······
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) {
srand(time(NULL));
int n, x;
cin >> n;
set<int> a;
vector<int> b;
vector<int> c;
for (int i = 0; i < n; ++i)
a.insert(i);
for (int i = 0; i < n; ++i) {
cin >> x;
if (a.count(x)) {
a.erase(x);
} else {
b.push_back(x);
}
}
for (auto i : a)
c.push_back(i);
sort(b.begin(), b.end());
for (int j = 0; j < (n / 40000.0) * 10000000.0; ++j) {
int l = rand() % b.size(), r = rand() % b.size();
if (sqrt(abs(b[r] - c[l])) + sqrt(abs(b[l] - c[r])) -
sqrt(abs(b[l] - c[l])) - sqrt(abs(b[r] - c[r])) <
0) {
auto tmp = b[l];
b[l] = b[r];
b[r] = tmp;
}
}
int p = 0;
for (int i = 0; i < n; ++i) {
if (a.count(i)) {
cout << b[p++] << " ";
} else {
cout << i << " ";
}
}
cout << endl;
}
return 0;
}
大概就是,先从小到大排序,然后每一次随机选择两个数,如果说交换这两个数能让差值函数更小,那就交换
奇迹般地水过了(也许不是水过的?)
第二场,我基本上也是以划为主吧······
C题直接n,m都小于等于4,可以直接枚举所有情况,队友猜了一个关于n*m奇偶性的结论,也对了
D题,直接模拟
K题:stack
题目大意:你有一个排列,你想将它依次插入栈中,并在栈不能保持递减的时候出栈,对于每一个时刻栈的大小求一个序列B,给出B中的几个元素,还原出一个满足这个B序列的A排列
这题队长过的,大概是下标从大到小开始计算,对于每一个i,求一个c[i],如果b[i]已知,那么c[i]=b[i],否则c[i]=b[i+1]-1
然后,a[i]就是当前序列中第c[i]小的数字,选中数字后还要将其删掉
以上过程可以用平衡树实现:
我们的初始化把自己的红黑树卡掉了,所以只好又写了一个splay,过了······
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using LL = long long;
using Tree = __gnu_pbds::tree<int, __gnu_pbds::null_type, less<int>,
__gnu_pbds::splay_tree_tag,
__gnu_pbds::tree_order_statistics_node_update>;
Tree t;
int b[1000001], ans[1000001];
void init(int n) {
for (int i = 1; i <= n; ++i)
t.insert(i);
}
int fun(int k) {
if (k <= 0) {
return 0;
}
auto tmp = t.find_by_order(k - 1);
int tmp1 = *tmp;
t.erase(tmp);
return tmp1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
int n = 0, k = 0;
cin >> n >> k;
int nn = n;
for (int i = 1; i <= k; ++i) {
int p, x;
cin >> p >> x;
b[p] = x;
}
for (n; n; --n) {
if (b[n]) {
break;
} else {
ans[n] = n;
}
}
init(n);
int now = 0;
for (int i = n; i; --i) {
--now;
if (b[i] && b[i] < now) {
cout << "-1\n";
return 0;
}
now = max(now, b[i]);
if (now > i) {
cout << "-1\n";
return 0;
}
ans[i] = fun(now);
}
for (int i = 1; i <= nn; ++i) {
if (!ans[i]) {
ans[i] = fun(1);
}
cout << ans[i] << ' ';
}
cout << '\n';
return 0;
}
I题:企鹅
大概就是两张20*20的地图上分别有一只企鹅,当左边的企鹅往右走的时候右边的企鹅往左走,反之亦然,两者可以同时往上走或者往下走,当一方被挡住了不妨碍另一方行走,并且双方必须同时到达终点,给出双方的起点
因为状态数很少,直接写了一个BFS
我被分配这道题的原因是······这道题代码量偏大······
但是写完发现其实没有很大
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int dyA[4] = {-1, 1, 0, 0};
int dxA[4] = {0, 0, -1, 1};
int dyB[4] = {1, -1, 0, 0};
int dxB[4] = {0, 0, -1, 1};
string opt[4] = {"L", "R", "U", "D"};
// L,R,U,D
struct state {
int a, b, c, d;
} st, en;
queue<state> q;
string k[20][20][20][20];
int step[20][20][20][20];
string as[20][2];
bool judge(int x, int y, int z) {
if (x < 0 || x >= 20)
return false;
if (y < 0 || y >= 20)
return false;
if (as[x][z][y] == '#')
return false;
return true;
}
bool operator==(state x, state y) {
return (x.a == y.a) && (x.b == y.b) && (x.c == y.c) && (x.d == y.d);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
memset(step, 0x3f, sizeof(step));
for (int i = 0; i < 20; i++)
cin >> as[i][0] >> as[i][1];
st.a = 19;
st.b = 19;
st.c = 19;
st.d = 0;
en.a = 0;
en.b = 19;
en.c = 0;
en.d = 0;
q.push(st);
step[st.a][st.b][st.c][st.d] = 0;
while (!q.empty()) {
state tmp = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
state nmp = tmp;
if (judge(tmp.a + dxA[i], tmp.b + dyA[i], 0))
nmp.a += dxA[i], nmp.b += dyA[i];
if (judge(tmp.c + dxB[i], tmp.d + dyB[i], 1))
nmp.c += dxB[i], nmp.d += dyB[i];
if (nmp == tmp)
continue;
int stepnmp = step[nmp.a][nmp.b][nmp.c][nmp.d];
int steptmp = step[tmp.a][tmp.b][tmp.c][tmp.d] + 1;
string knmp = k[nmp.a][nmp.b][nmp.c][nmp.d];
string ktmp = k[tmp.a][tmp.b][tmp.c][tmp.d] + opt[i];
if (stepnmp > steptmp || (stepnmp == steptmp && knmp > ktmp)) {
step[nmp.a][nmp.b][nmp.c][nmp.d] = steptmp;
k[nmp.a][nmp.b][nmp.c][nmp.d] = ktmp;
q.push(nmp);
}
}
}
cout << step[en.a][en.b][en.c][en.d] << '\n';
cout << k[en.a][en.b][en.c][en.d] << '\n';
// cerr << "mmp" << '\n';
state tmp = st;
as[tmp.a][0][tmp.b] = 'A';
as[tmp.c][1][tmp.d] = 'A';
string option = k[en.a][en.b][en.c][en.d];
int len = option.length();
for (int i = 0; i < len; i++) {
state nmp = tmp;
int x;
if (option[i] == 'L')
x = 0;
else if (option[i] == 'R')
x = 1;
else if (option[i] == 'U')
x = 2;
else
x = 3;
if (judge(tmp.a + dxA[x], tmp.b + dyA[x], 0))
nmp.a += dxA[x], nmp.b += dyA[x];
if (judge(tmp.c + dxB[x], tmp.d + dyB[x], 1))
nmp.c += dxB[x], nmp.d += dyB[x];
tmp = nmp;
as[tmp.a][0][tmp.b] = 'A';
as[tmp.c][1][tmp.d] = 'A';
}
for (int i = 0; i < 20; i++)
cout << as[i][0] << ' ' << as[i][1] << '\n';
return 0;
}
F题,Girlfriend
这个题关键在于读懂:
到一个点距离为到另一个点距离的k倍,这样的点组成一个圆
在三维空间中,这样的点组成一个球
题意即为求两个球的体积交
先通过给出的四个点和两个倍数k求出两个球的球心和半径
体积交的话······
我们其实忘了高数上讲的公式了
用微元法做的
一开始分了1000个微型圆柱体还没算对,后来改了5000个才过了
#include <cmath>
#include <iostream>
using namespace std;
using LD = long double;
const LD PI = M_PI;
struct P {
LD x, y, z;
explicit P(LD x = 0, LD y = 0, LD z = 0) : x(x), y(y), z(z) {}
};
struct B {
P p;
LD r;
} b0, b1;
LD dis(P a, P b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) +
(a.z - b.z) * (a.z - b.z));
}
LD fun(B a, LD y) {
LD t1 = a.r * cos(asin(y / a.r));
LD t = (a.r - t1) / 5000;
LD ans = 0;
for (LD i = t1; i <= a.r; i += t) {
ans += (a.r * a.r - i * i) * PI * t;
}
return ans;
}
LD fun1(B a, LD y) {
// return (PI - asin(y / a.r)) * 4 / 3 * a.r * a.r * a.r +
// PI * y * y * sqrt(a.r * a.r - y * y) / 3;
LD t1 = a.r * cos(asin(y / a.r));
LD t = (a.r - t1) / 5000;
LD ans = 0;
for (LD i = t1; i <= a.r; i += t) {
ans += (a.r * a.r - i * i) * PI * t;
}
return 4 * PI * a.r * a.r * a.r / 3 - ans;
}
int main() {
int T;
cin >> T;
for (; T; --T) {
LD ax, ay, az, bx, by, bz;
cin >> ax >> ay >> az;
cin >> bx >> by >> bz;
LD cx, cy, cz, dx, dy, dz;
cin >> cx >> cy >> cz;
cin >> dx >> dy >> dz;
LD k0, k1;
cin >> k0 >> k1;
b0.p = P((k0 * k0 * bx - ax) / (k0 * k0 - 1),
(k0 * k0 * by - ay) / (k0 * k0 - 1),
(k0 * k0 * bz - az) / (k0 * k0 - 1));
b0.r = sqrt(b0.p.x * b0.p.x + b0.p.y * b0.p.y + b0.p.z * b0.p.z +
(ax * ax + ay * ay + az * az -
k0 * k0 * (bx * bx + by * by + bz * bz)) /
(k0 * k0 - 1));
b1.p = P((k1 * k1 * dx - cx) / (k1 * k1 - 1),
(k1 * k1 * dy - cy) / (k1 * k1 - 1),
(k1 * k1 * dz - cz) / (k1 * k1 - 1));
b1.r = sqrt(b1.p.x * b1.p.x + b1.p.y * b1.p.y + b1.p.z * b1.p.z +
(cx * cx + cy * cy + cz * cz -
k1 * k1 * (dx * dx + dy * dy + dz * dz)) /
(k1 * k1 - 1));
LD d = dis(b0.p, b1.p);
if (d >= b0.r + b1.r) {
cout << "0\n";
} else if (d <= abs(b0.r - b1.r)) {
LD t = min(b0.r, b1.r);
cout << 4 * PI * t * t * t / 3 << '\n';
} else {
LD a = dis(b0.p, b1.p);
LD y = sqrt(2 * (b0.r * b0.r * a * a + b1.r * b1.r * a * a +
b0.r * b0.r * b1.r * b1.r) -
b0.r * b0.r * b0.r * b0.r - b1.r * b1.r * b1.r * b1.r -
a * a * a * a) /
2 / a;
LD rmin = min(b0.r, b1.r), rmax = max(b0.r, b1.r);
if (d * d + rmin * rmin >= rmax * rmax)
cout << fun(b0, y) + fun(b1, y) << "\n";
else {
if (b0.r < b1.r)
swap(b0, b1);
cout << fun(b0, y) + fun1(b1, y) << "\n";
}
}
}
return 0;
}