The 2017 China Collegiate Programming Contest, Qinhuangdao Site
Contest Info
Solved | A | B | C | D | E | F | G | H | I | J | K | L | M |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
9 / 13 | O | Ø | O | Ø | O | - | O | Ø | - | - | - | O | O |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A - Balloon Robot
题意:
\(n\)个人,\(m\)个座位组成一个圆环,\(1\leq n\leq 10^5,n\leq m\leq 10^9\)。
现在有\(q,q\leq 10^5\)个事件,表示第\(i\)个人在\(t_i\)时刻A了一道题。
\(s_i\)表示每个人的位置。
现在有个机器人,可以选定他开始的位置,时刻从\(1\)开始。之后他会顺时针走,每一秒走一步。如果他在某一个位置时这个人目前已经A了题,他将会发放气球。
记一个人的怒气值为\(得到气球的时间-A题的时间\)。
现在问起点选择哪个点使得所有人的怒气值之和最少。
思路:
- 机器人起点肯定位于某个人的位置,所以起点一共有\(n\)个。
- 每个事件对答案的贡献不会超过\(m\),其实可以将问题转化为一个模\(m\)意义下的问题。
- 先求出机器人一开始位于\(1\)位置的答案。之后枚举每个可能的起点计算答案即可。
- 机器人每移动一位所有事件的贡献在模意义下将减少\(1\),就相当于总答案减少\(p\)。那么枚举起点就可以\(O(1)\)转移答案了。
主要关键点在于将问题转化为模意义下的问题。
细节见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/18 16:22:01
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#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 = 1e5 + 5;
int s[N];
int d[N];
void run() {
int n,m,p;
cin >> n >> m >> p;
for(int i=1; i<=n; i++) {
cin >> s[i];
}
ll sum = 0;
for(int i=0; i<p; i++) {
int a,b; cin >> a >> b;
d[i] = ((s[a] - b)%m + m) % m;
sum += d[i];
}
ll ans = sum;
sort(d,d+p);
for(int i=0; i<p;) {
int now=i;
for(i++;i<p && d[i]==d[now]; i++);
int cnt=i-now;
ll res = sum - 1ll * p * d[now] + 1ll * now * m;
ans = min(ans, res);
}
cout << ans << '\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 - Expected Waiting Time
题意:
先有\(n\)个面试者,有\(2n\)个时刻表示每个面试者到来的时间以及面试官选择面试的时间(顺序不确定)。
当等候室内的人数不小于\(1\)时,面试官能够选择某个时间随机挑选一个人进行面试,最后\(n\)个人都要面试完。
设\(A\)为某个人到达的时间,\(S\)为面试官选择面试的事件,那么在任意一个时刻\(A\)的数量不小于\(S\)。显然有若干个合法的序列。最后求所有面试者等待的时间期望之和的平均值。
思路:
考虑对于任意一种合法的序列,期望之和为多少。显然此时和是不会改变的,不会因为面试官的选择而改变。那么面试官的随机选择我们可以看作一个固定选择,问题即转化为进出栈问题。
显然我们不可能枚举所有的情况,所以对于直接考虑一个位置为左括号、右括号的贡献,那么接下来我们只需要求出对应的情况数就行。接下来以右括号举例,左括号将序列翻转即可。
假设我们枚举到了位置\(j\),显然左边有个括号要与其匹配,我们枚举所有可能的位置\(j-1,j-3,j-5...\),从这些位置转移。那么中间的那些一定匹配成功,外面的那些也要匹配成功,所以就转化为了两个卡特兰数的乘积。即:
直接来求复杂度比较蛋疼。
注意到\(dp[j]=dp[j-2]+某一项\)。所以这样就可以\(O(n)\)枚举得到\(dp\)数组了。
接下来就很好办了,就按照刚刚的转化过程,逆着写得到期望之和就行。最后还要除以一个\(cat(n)\)。
代码如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/20 16:34:50
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#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 = 2e6 + 5;
int n, MOD;
ll A, B, a[N], b[N];
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;
}
int inv[N], cat[N];
void init(int n) {
inv[0] = inv[1] = 1;
for (int i = 2; i <= n + 1; i++) {
inv[i] = 1ll * inv[MOD % i] * (MOD - MOD / i) % MOD;
}
cat[0] = cat[1] = 1;
for (int i = 2; i <= n; i++) {
cat[i] = 1ll * cat[i - 1] * (4 * i - 2) % MOD * inv[i + 1] % MOD;
}
}
int dp[N];
void run() {
cin >> n >> MOD >> b[0] >> A >> B;
for (int i = 1; i <= 2 * n; i++) {
b[i] = (1ll * A * b[i - 1] % MOD + B) % MOD;
a[i] = (a[i - 1] + b[i] + 1) % MOD;
}
init(n);
for (int i = 2; i <= 2 * n; i++) {
dp[i] = (dp[i - 2] + 1ll * cat[i / 2 - 1] * cat[n - i / 2] % MOD) % MOD;
}
ll ans = 0;
for (int i = 1; i <= 2 * n; i++) {
if (i != 1) {
ans = (ans + 1ll * a[i] * dp[i] % MOD) % MOD;
}
if (i != 2 * n) {
ans = (ans - 1ll * a[i] * dp[2 * n - i + 1] % MOD) % MOD;
}
}
ans = 1ll * ans * qpow(cat[n], MOD - 2) % MOD;
if (ans < 0) ans += MOD;
cout << ans << '\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;
}
C - Crusaders Quest
签到。状压枚举所有情况即可。
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/18 13:21:12
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#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 = 1e5 + 5;
const char ss[] = {'g', 'a', 'o'};
void run() {
string s; cin >> s;
vector <int> p(3);
iota(all(p), 0);
int n;
auto check3 = [&] (string&s, int i) {
int j = i + 1, k = i + 2;
if (k >= n) return false;
return s[i] == s[j] && s[j] == s[k];
};
auto check2 = [&] (string&s, int i) {
int j = i + 1;
if (j >= n) return false;
return s[i] == s[j];
};
int ans = 0;
do {
n = 9;
int res = 0;
string t = s;
for (int k = 0; k < 3; k++) {
int flag = 1;
dbg(t, n);
for (int i = 0; i < n; i++) if (t[i] == ss[p[k]]) {
if (check3(t, i)) {
flag = 3;
break;
}
if (check2(t, i)) {
flag = 2;
break;
}
}
string tmp = "";
dbg(flag);
if (flag == 3) ++res;
for (int i = 0; i < n; i++) {
if (t[i] != ss[p[k]]) {
tmp += t[i];
}
}
swap(t, tmp);
n = t.length();
}
dbg(res);
ans = max(res, ans);
} while (next_permutation(all(p)));
cout << ans << '\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;
}
D - Graph Generator
题意:
如果已知一个排列\(p\),第\(i\)次操作产生\(p_i\)。然后可以选定目前图中若干个连通块,将\(p_i\)与连通块中所有的点进行连边。
现在给定最后的图,问是否存在一个\(p\)和每次选择的集合,使得最终能够得到这个图。
\(n,m\leq 10^5\)。
思路:
这种题从前往后很难构造,我们可以考虑从后往前在图中删点往前推。
观察到在一个连通块内,每次必然会先选择度数最大的点,因为当前选择的点要与其它所有点进行连边(注意是连通块)。
又有一个观察:每次去掉一个点后,其余所有的点度数都会减\(1\),也就是说度数的相对大小不变。
根据这两点做法呼之欲出了,我们只需要按照度数从大到小来处理。但现在还有个问题,连通块的问题,我们需要知道当前点所在连通块的size。
这种一般是并查集,但这里是删边,不好处理。所以。。直接上可撤销并查集就行了。先正着跑一遍,然后往前操作时不断从栈中取出之前状态进行还原。这样我们就能得到每一个时刻的连通状况。注意不能路径压缩,只能按秩合并。
细节见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/20 12:57:42
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#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 = 1e5 + 5;
struct UFS {
int f[N], h[N], sz[N], top;
struct node {
int x, y, fx, h, SZ;
}sta[N];
void init(int n) {
top = 0;
for (int i = 1; i <= n; i++) {
sz[i] = 1;
f[i] = i;
h[i] = 0;
}
}
int find(int x) {
return f[x] == x ? f[x] : find(f[x]);
}
bool merge(int u, int v) {
int x = find(u), y = find(v);
if (x == y) return false;
if (h[x] > h[y]) swap(x, y);
sta[++top] = node{x, y, f[x], h[y], sz[y]};
if (h[x] == h[y]) ++h[y];
sz[y] += sz[x];
f[x] = y;
return true;
}
void undo(int k) {
while (k--) {
node it = sta[top--];
f[it.x] = it.fx;
h[it.y] = it.h;
sz[it.y] = it.SZ;
}
}
int query(int x) {
int fx = find(x);
return sz[fx];
}
} ufs;
int n, m;
int d[N], a[N];
vector <int> G[N];
bool check[N];
void run() {
cin >> n >> m;
ufs.init(n);
for (int i = 1; i <= n; i++) {
d[i] = check[i] = 0;
G[i].clear();
a[i] = i;
}
for (int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
++d[u], ++d[v];
G[u].push_back(v);
G[v].push_back(u);
}
sort(a + 1, a + n + 1, [&](int i, int j) {
if (d[i] == d[j]) return i < j;
return d[i] < d[j];
});
vector <vector <int>> op(n + 1);
for (int i = 1; i <= n; i++) {
int u = a[i];
for (auto v : G[u]) {
if (d[v] <= d[u] && check[v]) {
if (ufs.merge(u, v)) {
op[u].push_back(v);
}
}
}
check[u] = true;
}
vector <pair<int, vector<int>>> ans;
for (int i = n; i >= 1; i--) {
int u = a[i];
int SZ = ufs.query(u);
if (d[u] != SZ - 1) {
cout << "No" << '\n';
return;
}
ans.push_back(MP(u, op[u]));
ufs.undo(sz(op[u]));
for (auto v : G[u]) {
--d[v];
}
}
cout << "Yes" << '\n';
reverse(all(ans));
for (auto it : ans) {
cout << it.fi << ' ' << sz(it.se);
for (auto it2 : it.se) {
cout << ' ' << it2;
}
cout << '\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;
}
E - String of CCPC
签到。答案最多增加\(1\),随便乱搞即可。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ld;
const int MAXN = 2e5 + 5, MAXM = 4e5 + 5, BOUND = 2e5, MOD = 1e9+7, INF = 0x3f3f3f3f, base = 10000;
const int inv2 = (MOD + 1) >> 1;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define lc(x) ch[x][0]
#define pii pair<int,int>
#define vi vector<int>
#define vii vector<pair<int,int>>
#define rc(x) ch[x][1]
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define all(a) (a).begin(), (a).end()
#define sz(a) int(a.size())
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fi first
#define se second
#define MP std::make_pair
#define ri register int
//#define sz(a) int((a).size())
inline int add(int a, int b) {return a + b >= MOD ? a + b - MOD : a + b;}
inline int dec(int a, int b) {return a < b ? a - b + MOD : a - b;}
inline int mul(int a, int b) {return 1ll * a * b % MOD;}
template <typename T>
inline void cmin(T &a,T b){a = min(a,b);}
template <typename T>
inline void cmax(T &a,T b){a = max(a,b);}
ll qpow(ll a,ll b){
ll ans=1;
for(;b;b>>=1,a=a*a%MOD)if(b&1)ans=ans*a%MOD;
return ans;
}
mt19937 mrand(random_device{}());
char s[MAXN];
int mp[5][2] = {{1,0},{2,0},{2,3},{4,0},{2,0}};
void run(){
int n;cin>>n;
cin>>(s+1);
int ans=0,sta=0;
bool flag=false;
rep(i,1,n){
if(sta==2 && s[i]=='C'){
if(!flag){
flag=true;
ans++;
}
}
if(sta==1 && s[i]=='P' && i+1<=n && s[i+1]=='C'){
if(!flag){
flag=true;
ans++;
//cout<<"lalala\n";
}
}
if(s[i]=='C'){
sta = mp[sta][0];
}else if(s[i]=='P'){
sta = mp[sta][1];
}
if(sta==4)ans++;
if(i==n && sta==3){
if(!flag){
flag=true;
ans++;
}
}
//cout<<sta<<" \n"[i==n];
//cout<<ans<<" \n"[i==n];
}
cout<<ans<<'\n';
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int _;cin>>_;
while(_--)run();
return 0;
}
G - Numbers
二进制位从高到低贪心即可。涉及到大数,所以用的python。
Code
t = int(input())
for i in range(t):
n, m = map(int, input().split())
pw = []
i = 1
while i <= n:
pw.append(i)
i *= 2
ans = 0
mx = 0
for i in range(len(pw)-1, -1, -1):
if mx + m*(pw[i]-1) < n:
ans += pw[i]
mx += min(m, (n-mx)//pw[i])*pw[i]
#print(pw[i], mx)
print(ans)
H - Prime Set
题意:
定义一个素数集为\(\{i,j\}\)并且\(a_i+a_j\)为素数。
给定序列\(a\),现从中选择至多\(k\)个素数集,使得并起来的元素个数最多。
\(n\leq 10^3,0\leq k\leq \frac{n\cdot(n-1)}{2}\)。
思路:
利用“两个数加起来为素数”这个条件,我们容易发现除开\(1+1=2\)这种情况,其余都是一奇一偶。这有点二分图的意思。
所以考虑建二分图,奇数在左边,偶数在右边,然后按照题意要求进行连边求个最大匹配即可。
注意\(1\)的存在比较特殊,应该最后再从\(1\)出发跑增广路。这样答案能够更优。
最后再\((1,1)\)进行匹配。
细节见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/18 19:30:23
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#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 = 2e6 + 5;
int v[N], prime[N];
int num;
void init() {
for(int i = 2; i < N; i++) {
if(v[i] == 0) {
v[i] = i;
prime[++num] = i;
}
for(int j = 1; j <= num && prime[j] * i < N; j++) {
v[prime[j] * i] = prime[j] ;
}
}
}
int n, k;
int a[N];
int Match[N];
struct MaxMatch {
int n;
vector<int> G[N];
int vis[N], left[N], clk;
void init(int n) {
this->n = n;
for (int i = 1; i <= n; i++) G[i].clear();
fill(vis + 1, vis + n + 1, -1);
}
void adde(int u, int v) {
G[u].push_back(v);
G[v].push_back(u);
}
bool dfs(int u) {
for (int v: G[u])
if (vis[v] != clk) {
vis[v] = clk;
if (Match[v] == -1 || dfs(Match[v])) {
Match[u] = v;
Match[v] = u;
return true;
}
}
return false;
}
int solve() {
int res = 0;
for (int i = 1; i <= n; i++) {
if ((a[i] & 1) && a[i] > 1) {
++clk;
res += dfs(i);
}
}
for (int i = 1; i <= n; i++) {
if (a[i] == 1) {
++clk;
res += dfs(i);
}
}
int x = 0;
for (int i = 1; i <= n; i++) {
if (a[i] == 1 && Match[i] < 0) {
++x;
Match[i] = 0;
}
}
return res + x / 2;
}
} MM;
void run() {
cin >> n >> k;
MM.init(n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
Match[i] = -2;
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (v[a[i] + a[j]] == a[i] + a[j]) {
Match[i] = Match[j] = -1;
if (a[i] == 1 && a[j] == 1) continue;
MM.adde(i, j);
}
}
}
int t = MM.solve();
if (t < k) {
int noMat = 0;
for (int i = 1; i <= n; i++) {
if (Match[i] == -2) {
++noMat;
}
}
int r = n - 2 * t - noMat;
cout << 2 * t + min(r, k - t) << '\n';
} else {
cout << 2 * k << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
init();
int T; cin >> T; while(T--)
run();
return 0;
}
L - One-Dimensional Maze
签到。
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/18 13:08:04
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#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 = 1e5 + 5;
void run() {
int n, m; cin >> n >> m; --m;
string s; cin >> s;
vector <int> sum(n);
sum[0] = (s[0] == 'L');
for (int i = 1; i < n; i++) {
sum[i] = sum[i - 1] + (s[i] == 'L');
}
int ans = min(m + 1 - sum[m] - (s[0] == 'R'), sum[n - 2] - sum[m - 1]);
ans = max(ans, 0);
cout << ans << '\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;
}
重要的是自信,一旦有了自信,人就会赢得一切。