“科大讯飞杯”第十七届同济大学程序设计预选赛暨高校网络友谊赛
A.张老师和菜哭武的游戏
题意:
现有数为\(1\)~\(n\)的集合,两个人从中选出\(a,b,a\not ={b}\),然后两个人依次轮流从集合选出一个数\(z\),满足\(z=x+y,z=x-y,x,y\)为已选出的两个数。
问是否第一个人能够获胜。
思路:
显然最后所有选出的数都为\(xa+yb\)的形式。
我们根据贝祖定理,会发现\(xa+yb\)有整数解时,其必然为\(gcd(a,b)\)的倍数,即\(xa+yb=t\cdot gcd(a,b)\)。
所以就算出\(gcd\)然后判断一下就行。
代码如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/5/10 13:33:47
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 10000 + 5;
bool chk[N];
void run() {
int n, a, b; cin >> n >> a >> b;
if (a > b) swap(a, b);
if (b % a == 0) {
int t = n / a;
if (t & 1) {
cout << "Yes" << '\n';
} else {
cout << "No" << '\n';
}
} else {
if (a & 1 || b & 1) {
if (n & 1) {
cout << "Yes" << '\n';
} else cout << "No" << '\n';
} else {
int t = n / 2;
if (t & 1) {
cout << "Yes" << '\n';
} else {
cout << "No" << '\n';
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
B.伤害计算
随便算一算。
Code
/*
* Author: heyuhhh
* Created Time: 2020/5/10 13:57:43
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5000 + 5;
char s[N];
int n;
const double eps = 1e-6;
void run() {
cin >> (s + 1);
n = strlen(s + 1);
double res = 0;
for (int i = 1, j; i <= n; i = j) {
j = i;
int x = 0, y = 0;
while (j <= n && s[j] <= '9' && s[j] >= '0') {
x = x * 10 + s[j] - '0';
++j;
}
if (j <= n && s[j] == '+') {
res += x;
++j; continue;
} else if(j > n) {
res += x;
continue;
}
++j;
while (j <= n && s[j] <= '9' && s[j] >= '0') {
y = y * 10 + s[j] - '0';
++j;
}
res += 1.0 * x * (y + 1) * y / 2 / y;
}
if (fabs(res - round(res)) <= eps) {
cout << (ll) res << '\n';
} else {
cout << fixed << setprecision(1) << res << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
run();
return 0;
}
C.张老师的旅行
题意:
数轴上给定\(n,n\leq 1000\)个点,每个点的位置为\(p_i\)。
起点位于\(x\),现在有个人从起点出发,要遍历所有的点,走一单位路程花费一单位时间。问遍历所有点最少花多少时间。
思路:
我们容易发现这样一个性质:
- 每次这个人会遍历完一段区间\([l,r]\)中的点。
那么这就是一个比较套路的\(dp\)了,我们直接用\(dp_{l,r,0/1}\)表示遍历完区间\([l,r]\),当前在区间的左端点/右端点的最短花费。转移向左向右两种情况转移即可。
其实也就是个最短路问题,赛场上我写了一下dijkstra。
代码如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/5/10 14:56:58
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e3 + 5;
int n, x;
int p[N], t[N];
ll dis[N][N];
struct Dijkstra{
struct node{
ll d;
pii sta;
bool operator < (const node &A) const {
return d > A.d;
}
};
bool vis[N][N];
void dij(int s) {
priority_queue <node> q;
memset(dis, INF, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[s][1] = 0;
q.push(node{0, MP(s, 1)});
while(!q.empty()) {
node cur = q.top(); q.pop();
ll d = cur.d;
pii sta = cur.sta;
int now = sta.fi, had = sta.se;
if(vis[now][had]) continue;
vis[now][had] = 1;
dis[now][had] = d;
if (now <= x) {
if (now + had <= n && p[now + had] - p[now] + d <= t[now + had]) {
q.push(node{p[now + had] - p[now] + d, MP(now + had, had + 1)});
}
if (now > 1 && d + p[now] - p[now - 1] <= t[now - 1]) {
q.push(node{d + p[now] - p[now - 1], MP(now - 1, had + 1)});
}
} else {
if (now - had >= 1 && p[now] - p[now - had] + d <= t[now - had]) {
q.push(node{p[now] - p[now - had] + d, MP(now - had, had + 1)});
}
if (now < n && d + p[now + 1] - p[now] <= t[now + 1]) {
q.push(node{d + p[now + 1] - p[now], MP(now + 1, had + 1)});
}
}
}
}
}solver;
void run() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> p[i];
}
for (int i = 1; i <= n; i++) {
cin >> t[i];
if (t[i] == 0) x = i;
}
solver.dij(x);
ll ans = min(dis[1][n], dis[n][n]);
if (ans == dis[0][0]) ans = -1;
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
D.车辆调度
由于数据范围很小,直接\(dfs\)枚举所有的可能性就可。
Code
/*
* Author: heyuhhh
* Created Time: 2020/5/10 14:34:55
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 10 + 1;
int w, h, k;
char s[N][N];
void dfs(int cur, char s[][N]) {
if (cur == k) return;
for (int i = 1; i <= w; i++) {
for (int j = 1; j <= h; j++) if (s[i][j] == 'R') {
for (int k = j; k >= 1; k--) {
if (k - 1 < 1 || s[i][k - 1] == 'X' || s[i][k - 1] == 'R') {
if (s[i][k] == 'D') {
cout << "YES" << '\n';
exit(0);
}
swap(s[i][k], s[i][j]);
dfs(cur + 1, s);
swap(s[i][k], s[i][j]);
break;
}
}
for (int k = j; k <= h; k++) {
if (k + 1 > h || s[i][k + 1] == 'X' || s[i][k + 1] == 'R') {
if (s[i][k] == 'D') {
cout << "YES" << '\n';
exit(0);
}
swap(s[i][k], s[i][j]);
dfs(cur + 1, s);
swap(s[i][k], s[i][j]);
break;
}
}
for (int k = i; k >= 1; k--) {
if (k - 1 < 1 || s[k - 1][j] == 'X' || s[k - 1][j] == 'R') {
if (s[k][j] == 'D') {
cout << "YES" << '\n';
exit(0);
}
swap(s[k][j], s[i][j]);
dfs(cur + 1, s);
swap(s[k][j], s[i][j]);
break;
}
}
for (int k = i; k <= w; k++) {
if (k + 1 > w || s[k + 1][j] == 'X' || s[k + 1][j] == 'R') {
if (s[k][j] == 'D') {
cout << "YES" << '\n';
exit(0);
}
swap(s[k][j], s[i][j]);
dfs(cur + 1, s);
swap(s[k][j], s[i][j]);
break;
}
}
}
}
}
void run() {
cin >> w >> h >> k;
swap(w, h);
for (int i = 1; i <= w; i++) {
cin >> (s[i] + 1);
}
dfs(0, s);
cout << "NO" << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
E.弦
题意:
给定一个圆,圆上有2N个互不重叠的点。每次操作随机选择两个先前未选择过的点连一条弦,共连成N条弦,求所有弦不交的概率。
思路:
我们直接用合法情况除以总情况即可。
合法情况就是一个卡特兰数,为什么呢。我们用\(f(i)\)表示\(i\)对点的情况数,那么转移就是\(\displaystyle f(i)=\sum_{k=0}^{i-1}f(k)\cdot f(i-1-k)\)。这就是卡特兰数的递推式。
总的情况组合数算一算就行。
Code
/*
* Author: heyuhhh
* Created Time: 2020/5/10 16:20:55
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e7 + 5, MOD = 1e9 + 7;
int qpow(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
void run() {
int n; cin >> n;
int fac = 1;
for (int i = 1; i <= n + 1; i++) fac = 1ll * fac * i % MOD;
int ans = 1ll * qpow(2, n) * qpow(fac, MOD - 2) % MOD;
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
F.排列计算
签到。
Code
/*
* Author: heyuhhh
* Created Time: 2020/5/10 13:12:25
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5;
int n, m;
int a[N];
void run() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int l, r; cin >> l >> r;
++a[l], --a[r + 1];
}
for (int i = 1; i <= n; i++) {
a[i] += a[i - 1];
}
sort(a + 1, a + n + 1);
ll ans = 0;
for (int i = n; i >= 1; i--) {
ans += 1ll * i * a[i];
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
I.纸牌
题意:
桌上有一叠共N张牌,从顶向下标号为1~N。张老师打算对这一叠牌做k次操作,其中第i次操作会将牌堆顶的牌放在牌堆中的某个位置,从而满足这张牌成为自顶向下第(i - 1) % (N - 1) + 2张牌,其中%表示取模操作。张老师想知道k次操作以后这叠牌自顶向下的编号情况,你能告诉他吗?
\(n\leq 10^6,k\leq 10^{18}\)。
思路:
我们首先来考虑\(k<n\)的情况,我们可以用一个链表来模拟,但是复杂度较高,我们可以观察一下规律:如果我们在\(1,3,5,...\)依次放上\(1,2,3,...\),空出\(2,4,6,...\)。
那么我们会发现第\(i\)次操作会放在\(2\cdot (i+1)\)个位置,也就是每次放的位置都是递增的。我们就这样模拟的话时间复杂度为\(O(n)\)的。
那更大的情况呢?
这里我们可以直接通过倍增置换来解决。假设我们已经经过了\(n-1\)次置换,置换的排列为\(p[i]\),即\(i\rightarrow p[i]\)。那么我们可以直接通过\(p[p[i]]\)求出\(2n-2\)次置换。那么这里我们可以类似于快速幂那样进行倍增。
最后剩余我们可以直接类似于上面那样进行模拟。
总时间复杂度\(O(nlog(\frac{k}{n-1}))\)。
细节见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/5/11 10:42:59
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
int n;
ll k;
int base[N], a[N];
int tmp[N << 1], res[N];
void Iter(int x, int* a) {
for (int i = 1; i<= n; i++) {
tmp[2 * i - 1] = a[i];
tmp[2 * i] = 0;
}
int p = 1;
for (int i = 1; i <= x; i++) {
while (tmp[p] == 0) ++p;
swap(tmp[p], tmp[(i + 1) * 2]);
}
int t = 0;
for (int i = 1; i <= 2 * n; i++) {
if (tmp[i] > 0) {
a[++t] = tmp[i];
}
}
}
void mul(int* a, int* b) {
for (int i = 1; i <= n; i++) {
res[i] = a[b[i]];
}
for (int i = 1; i <= n; i++) {
a[i] = res[i];
}
}
void run() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
base[i] = a[i] = i;
}
ll t = k / (n - 1), r = k % (n - 1);
Iter(n - 1, base);
while (t) {
if (t & 1) mul(a, base);
mul(base, base);
t >>= 1;
}
Iter(r, a);
for (int i = 1; i <= n; i++) {
cout << a[i] << " \n"[i == n];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
J.斐波那契和
题意:
求\(\displaystyle\sum_{i=1}^ni^kFib(i),n\leq 10^{18},k\leq 100\)。
思路:
就算出前面若干项(好像400项就行),然后扔进BM里面结果就出来了。
具体为啥我也不是很清楚。
Code
/*
* Author: heyuhhh
* Created Time: 2020/5/10 14:16:41
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5, MOD = 998244353;
int fib[N];
void init() {
fib[1] = fib[2] = 1;
for (int i = 3; i < N; i++) {
fib[i] = (fib[i - 1] + fib[i - 2]) % MOD;
}
}
ll n, k;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
ll powMOD(ll a, ll b) {
ll ans = 1;
for (; b; b >>= 1, a = a * a%MOD)if (b & 1)ans = ans * a%MOD;
return ans;
}
namespace linear_seq {
ll res[N], base[N], _c[N], _md[N];
vector<int> Md;
void mul(ll *a, ll *b, int k) {
rep(i, 0, k + k) _c[i] = 0;
rep(i, 0, k) if (a[i]) rep(j, 0, k) _c[i + j] = (_c[i + j] + a[i] * b[j]) % MOD;
for (int i = k + k - 1; i >= k; i--) if (_c[i])
rep(j, 0, SZ(Md)) _c[i - k + Md[j]] = (_c[i - k + Md[j]] - _c[i] * _md[Md[j]]) % MOD;
rep(i, 0, k) a[i] = _c[i];
}
int solve(ll n, VI a, VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+...
ll ans = 0, pnt = 0;
int k = SZ(a);
assert(SZ(a) == SZ(b));
rep(i, 0, k) _md[k - 1 - i] = -a[i]; _md[k] = 1;
Md.clear();
rep(i, 0, k) if (_md[i] != 0) Md.push_back(i);
rep(i, 0, k) res[i] = base[i] = 0;
res[0] = 1;
while ((1ll << pnt) <= n) pnt++;
for (int p = pnt; p >= 0; p--) {
mul(res, res, k);
if ((n >> p) & 1) {
for (int i = k - 1; i >= 0; i--) res[i + 1] = res[i]; res[0] = 0;
rep(j, 0, SZ(Md)) res[Md[j]] = (res[Md[j]] - res[k] * _md[Md[j]]) % MOD;
}
}
rep(i, 0, k) ans = (ans + res[i] * b[i]) % MOD;
if (ans < 0) ans += MOD;
return ans;
}
VI BM(VI s) {
VI C(1, 1), B(1, 1);
int L = 0, m = 1, b = 1;
rep(n, 0, SZ(s)) {
ll d = 0;
rep(i, 0, L + 1) d = (d + (ll)C[i] * s[n - i]) % MOD;
if (d == 0) ++m;
else if (2 * L <= n) {
VI T = C;
ll c = MOD - d * powMOD(b, MOD - 2) % MOD;
while (SZ(C) < SZ(B) + m) C.pb(0);
rep(i, 0, SZ(B)) C[i + m] = (C[i + m] + c * B[i]) % MOD;
L = n + 1 - L; B = T; b = d; m = 1;
}
else {
ll c = MOD - d * powMOD(b, MOD - 2) % MOD;
while (SZ(C) < SZ(B) + m) C.pb(0);
rep(i, 0, SZ(B)) C[i + m] = (C[i + m] + c * B[i]) % MOD;
++m;
}
}
return C;
}
int gao(VI& a, ll n) {
VI c = BM(a);
c.erase(c.begin());
rep(i, 0, SZ(c)) c[i] = (MOD - c[i]) % MOD;
return solve(n, c, VI(a.begin(), a.begin() + SZ(c)));
}
};
void run() {
init();
cin >> n >> k;
vector <int> f(N);
f[0] = 0;
for (int i = 1; i < N; i++) {
f[i] = (f[i - 1] + 1ll * powMOD(i, k) * fib[i] % MOD) % MOD;
}
int res = linear_seq::gao(f, n);
cout << res << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
重要的是自信,一旦有了自信,人就会赢得一切。