第十六届北京师范大学程序设计竞赛决赛(网络同步赛)
题目链接 第十六届北京师范大学程序设计竞赛决赛
一句话总结:迟到选手抢到FB之后进入梦游模式最后因为忘加反向边绝杀失败……
好吧其实还是自己太弱
下面进入正题
Problem A
签到题(读题是一件非常有趣事情)
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
int main(){
int T; string s;
scanf("%d", &T);
while (T--){
int n;
scanf("%d", &n);
int fg = 1;
rep(i, 1, n){
cin >> s;
if (s != "PERFECT") fg = 0;
}
puts(fg ? "MILLION Master" : "NAIVE Noob");
}
return 0;
}
Problem B
设读进来的那个序列为$b_{i}$
要还原出的那个序列答案为$a_{i}$
我们求出$a_{i}$对$b_{i}$每一项的贡献就可以了。
这个过程实现起来不难。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
typedef long long LL;
const LL mod = 1e9 + 7;
const int N = 1e3 + 10;
int T;
int n;
LL a[N], b[N], c[N];
LL k;
inline LL Pow(LL a, LL b, LL mod){
LL ret(1);
for (; b; b >>= 1, (a *= a) %= mod) if (b & 1) (ret *= a) %= mod;
return ret;
}
void pre(){
int cnt = 0;
c[++cnt] = 1;
LL now = 1;
rep(i, 1, n - 1){
now = now * (k + i - 1) % mod;
now = now * Pow((LL)i, mod - 2, mod) % mod;
c[++cnt] = now;
}
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%lld", &n, &k);
memset(c, 0, sizeof c);
pre();
rep(i, 1, n) scanf("%lld", b + i);
a[1] = b[1];
rep(i, 1, n){
int cnt = 0;
rep(j, i, n){
++cnt;
b[j] -= (c[cnt] * a[i] % mod);
b[j] += mod;
b[j] %= mod;
}
a[i + 1] = b[i + 1];
}
rep(i, 1, n - 1) printf("%lld ", a[i]);
printf("%lld\n", a[n]);
}
return 0;
}
Problem C
打表之后找规律
#include<bits/stdc++.h>
using namespace std;
int T;
int n;
int main(){
cin >> T;
while (T--){
cin >> n;
cout << fixed << setprecision(10) << (n * n - 1) / 3.0 << endl;
}
return 0;
}
Problem D
这个DP转移
每个点有两种转移的方向,每个方向取最近的那个点就好了。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
const int N = 1e5 + 10;
int T;
int n, m, k;
LL a[N], b[N], c[N], f[N], g[N], h[N];
map <LL, LL> mp;
set <LL> s;
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%d%d", &n, &m, &k);
rep(i, 1, n) scanf("%lld", a + i);
rep(i, 1, m) scanf("%lld", b + i);
rep(i, 1, k) scanf("%lld", c + i);
rep(i, 1, n) f[i] = 1;
rep(i, 1, m) g[i] = 1e15;
mp.clear();
rep(i, 1, n) mp[a[i]] = f[i];
mp[1e18] = 1e18, mp[-1e18] = 1e18;
s.clear();
rep(i, 1, n) s.insert(a[i]);
s.insert(1e18);
s.insert(-1e18);
rep(i, 1, m){
LL xx = b[i];
auto it = s.lower_bound(xx);
g[i] = min(g[i], mp[*it] + abs((*it) - xx) + 1);
--it;
g[i] = min(g[i], mp[*it] + abs((*it) - xx) + 1);
}
rep(i, 1, k) h[i] = 1e15;
mp.clear();
rep(i, 1, m) mp[b[i]] = g[i];
mp[1e18] = 1e18, mp[-1e18] = 1e18;
s.clear();
rep(i, 1, m) s.insert(b[i]);
s.insert(1e18);
s.insert(-1e18);
rep(i, 1, k){
LL xx = c[i];
auto it = s.lower_bound(xx);
h[i] = min(h[i], mp[*it] + abs((*it) - xx) + 1);
--it;
h[i] = min(h[i], mp[*it] + abs((*it) - xx) + 1);
}
LL ans = 1e18;
rep(i, 1, k) ans = min(ans, h[i]);
printf("%lld\n", ans);
}
return 0;
}
Problem E
考虑到长度为$k$的子序列个数不超过$10^{5}$,那么直接暴力,搜索深度不超过$9$。
注意:$k$比较大的时候考虑反面即可。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
const int N = 1e5 + 10;
int T;
int n, k;
int z;
int c[N];
LL a[N];
LL ans;
void check(LL x){
LL vv = x * x;
ans ^= vv;
}
void dfs(int x, LL now){
if (x > k){
check(now);
return;
}
else{
rep(i, z, n){
int la = z;
z = i + 1;
dfs(x + 1, now + a[i]);
z = la;
}
}
}
void dfs2(int x, LL now){
if (x > k){
check(now);
return;
}
else{
rep(i, z, n){
int la = z;
z = i + 1;
dfs2(x + 1, now - a[i]);
z = la;
}
}
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &k);
LL sum = 0;
rep(i, 1, n) scanf("%lld", a + i), sum += a[i];
z = 1;
ans = 0;
if (k > n - k){
k = n - k;
dfs2(1, sum);
}
else{
dfs(1, 0);
}
printf("%lld\n", ans);
}
return 0;
}
Problem F
显然答案是单调的。
二分答案,把那些符合条件的汤圆一个个加入队列,然后BFS,到最后如果所有汤圆都被删除了,那么该答案可行。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
typedef pair <int, LL> PII;
const int N = 1e5 + 10;
int T;
int n, m;
int vis[N], inq[N];
LL a[N], bb[N];
LL l, r;
vector <PII> v[N];
bool check(LL now){
queue <int> q;
memset(vis, 0, sizeof vis);
memset(inq, 0, sizeof inq);
rep(i, 1, n) bb[i] = a[i];
rep(i, 1, n) if (bb[i] <= now) q.push(i), inq[i] = 1;
while (!q.empty()){
int x = q.front();
vis[x] = 1;
q.pop();
inq[x] = 0;
for (auto edge : v[x]){
int u = edge.fi;
LL w = edge.se;
if (vis[u]) continue;
bb[u] -= w;
if (bb[u] <= now){
if (!inq[u]) q.push(u), inq[u] = 1;
}
}
}
rep(i, 1, n) if (!vis[i]) return false;
return true;
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
rep(i, 0, n + 1) v[i].clear();
rep(i, 0, n + 1) a[i] = 0;
rep(i, 1, m){
int x, y;
LL z;
scanf("%d%d%lld", &x, &y, &z);
v[x].push_back({y, (LL)z});
v[y].push_back({x, (LL)z});
a[x] += z;
a[y] += z;
}
l = 0, r = 1e14;
while (l + 1 < r){
LL mid = (l + r) / 2ll;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (check(l)) printf("%lld\n", l);
else printf("%lld\n", r);
}
return 0;
}
Problem G
模拟题,没什么好说的。(我还是WA了好几发)
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
char s[24];
bool ff[24];
int T;
bool upper(char c){ return c >= 'A' && c <= 'Z'; }
bool check(int n){
bool re = 1;
bool flag = 0;
int cnt = 0;
if (!upper(s[0])) s[0] += 'A' - 'a', flag = 1;
rep(i, 1, n - 1){
if (upper(s[i]) && upper(s[i - 1])) re = 0;
if (upper(s[i])) cnt++, ff[i] = 1;
}
if (flag) s[0] += 'a' - 'A';
return re && (cnt) && !upper(s[n - 1]);
}
int main(){
cin >> T;
while (T--){
scanf("%s", &s);
memset(ff, 0, sizeof ff);
int n = strlen(s);
if (check(n)){
rep(i, 0, n - 1){
if (ff[i]) putchar('_');
if (upper(s[i])) s[i] += 'a' - 'A';
putchar(s[i]);
}
putchar(10);
}
else puts(s);
}
return 0;
}
Problem H
数据结构题,留坑。
Problem I
每次交换相邻的两个不一样的元素会使整个序列的逆序对数改变$1$
那么计算一下逆序对个数就好了。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
const int N = 1e6 + 10;
int T;
int n;
int a[N];
char s[N];
LL k;
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%lld", &n, &k);
scanf("%s", s + 1);
rep(i, 1, n) a[i] = (s[i] == 'D');
LL cc = 0;
rep(i, 1, n) cc += a[i];
LL ju = 1ll * cc * (LL)(n - cc);
if (ju < k) {
puts("-1");
continue;
}
LL cnt = 0, ss = 0;
rep(i, 1, n){
if (a[i] == 0) cnt += ss;
ss += a[i];
}
printf("%lld\n", abs(cnt - k));
}
return 0;
}
Problem J
计算几何,留坑。
Problem K
留坑。