2020 BUPT Winter Training #1 Div.1
这些题真的很不错
读完题一道都不会
补完每一道都骂自己sb
文章目录
- [A - Cover it!](https://vjudge.net/problem/CodeForces-1176E) [CodeForces - 1176E ](https://vjudge.net/problem/2485867/origin)
- [B - Vasya And Array](https://vjudge.net/problem/CodeForces-1187C) [CodeForces - 1187C ](https://vjudge.net/problem/2515423/origin)
- [C - Tree Painting](https://vjudge.net/problem/CodeForces-1187E) [CodeForces - 1187E ](https://vjudge.net/problem/2515428/origin)
- [G - Restore Permutation](https://vjudge.net/problem/CodeForces-1208D) [CodeForces - 1208D ](https://vjudge.net/problem/2704063/origin)
- [I - Polygons](https://vjudge.net/problem/CodeForces-1208G) [CodeForces - 1208G ](https://vjudge.net/problem/2704066/origin)
- [E - Tokitsukaze and Duel](https://vjudge.net/problem/CodeForces-1190C) [CodeForces - 1190C ](https://vjudge.net/problem/2548326/origin)
- [D - Expected Square Beauty](https://vjudge.net/problem/CodeForces-1187F) [CodeForces - 1187F ](https://vjudge.net/problem/2515429/origin)
- 最后
A - Cover it! CodeForces - 1176E
**题意:**给你一个n个点m条边的无向连通图,让你选最多n/2个点,使每个没有被选的点都与至少一个被选的点相邻。
**思路:**一开始老想贪心 真™sb。实际上只需要建树,选奇数层,如果奇数层的点数大于2/n则选偶数层。
代码:
#include <bits/stdc++.h>
#define MAXN 200010
using namespace std;
typedef pair<int, int> P;
vector <int> a[MAXN], ans1, ans2;
int vis[MAXN];
void dfs(int now, int flag){
flag *= -1;
for(int i = 0; i < a[now].size(); i++)
if(!vis[a[now][i]]){
vis[a[now][i]] = flag;
dfs(a[now][i], flag);
}
}
void solve(){
int n, m, u, v;
scanf("%d%d", &n, &m);
while(m--){
scanf("%d%d", &u, &v);
a[u].push_back(v); a[v].push_back(u);
}
vis[1] = 1;
dfs(1, 1);
for(int i = 1; i <= n; i++){
if(vis[i] == 1) ans1.push_back(i);
else ans2.push_back(i);
a[i].clear();
vis[i] = 0;
}
if(ans1.size() < ans2.size()){
printf("%d\n", ans1.size());
for(int i = 0; i < ans1.size(); i++) printf("%d ", ans1[i]);
printf("\n");
}
else{
printf("%d\n", ans2.size());
for(int i = 0; i < ans2.size(); i++) printf("%d ", ans2[i]);
printf("\n");
}
ans1.clear(); ans2.clear();
}
int main(){
int T; scanf("%d", &T);
while(T--) solve();
}
B - Vasya And Array CodeForces - 1187C
**题意:**给你m个条件,让你构造一个满足条件的数列。条件有两种:
- t = 1时,a[l],……,a[r]非严格递增
- t = 0时,a[l],……,a[r]不是非严格递增。
**思路:**非严格递增区间可以视作区间内的数都相同,那么我们可以将非严格递增区间的元素都标记一下(vis)。如果存在t = 0时,vis[l]~vis[r]都标记过,那么无法构造。否则将其中一个位置另外标记,表示这里递减一下,这样就可以满足t = 0的条件了。
代码:
#include <bits/stdc++.h>
#define MAXN 1010
#define mp make_pair
using namespace std;
typedef pair<int, int> P;
int vis[MAXN];
vector <P> t0, t1;
int main(){
int n, m, t, l, r;
scanf("%d%d", &n, &m);
while(m--){
scanf("%d%d%d", &t, &l, &r);
if(t == 1) t1.push_back(mp(l, r));
else t0.push_back(mp(l, r));
}
sort(t1.begin(), t1.end());
int now = -1;
for(int i = 0; i < t1.size(); i++){
l = t1[i].first, r = t1[i].second - 1;
if(l > now) now = l, vis[l] = 1;
if(r > now){
while(now <= r) vis[now++] = 1;
--now;
}
}
for(int i = 0; i < t0.size(); i++){
l = t0[i].first, r = t0[i].second - 1;
bool flag = false;
for(int j = l; j <= r; j++) if(vis[j] != 1) vis[j] = -1, flag = true;
if(!flag){ printf("NO\n"); return 0; }
}
printf("YES\n");
now = n;
for(int i = 1; i <= n; i++){
printf("%d ", now);
if(vis[i] == -1) --now;
}
}
C - Tree Painting CodeForces - 1187E
这是一道树形dp,我不仅见过,补过题,还写过题解贴过代码。可是现在还是不会……
https://yzq2000.blog.youkuaiyun.com/article/details/94672034
G - Restore Permutation CodeForces - 1208D
这还是一道构造题。
题意: 有一个隐藏的排列a,对于位置i,你知道在i前面比a[i]小的数的和s[n]。让你恢复这个排列。
思路: s[n]其实就是1~n中比a[n]小的数的和,所以可以求得a[n]。从后往前找,对于a[i],我们可以知道它前面可能有哪些数,s[i]就是那些数的前缀和。用线段树来实现前缀和的修改与查询即可。
代码:
#include <bits/stdc++.h>
#define ll long long
#define MAXN 200010
using namespace std;
ll s[MAXN], sum[MAXN], Min[MAXN << 3], lazy[MAXN << 3];
int Ans[MAXN];
inline int lson(int node){ return node << 1;}
inline int rson(int node){ return node << 1 | 1;}
void push_down(int node){
if(lazy[node]){
lazy[lson(node)] += lazy[node];
lazy[rson(node)] += lazy[node];
Min[node] -= lazy[node];
lazy[node] = 0;
}
}
void push_up(int node){
Min[node] = min(Min[lson(node)], Min[rson(node)]);
}
void build(int node, int l, int r){
if(l == r){ Min[node] = sum[l]; return;}
int mid = (l + r) >> 1;
build(lson(node), l, mid);
build(rson(node), mid + 1, r);
push_up(node);
}
void query_Min(int node, int l, int r, ll x, int &ans){
if(l == r){ ans = l; return; }
push_down(node);
push_down(lson(node));
push_down(rson(node));
int mid = (l + r) >> 1;
if(Min[rson(node)] <= x) query_Min(rson(node), mid + 1, r, x, ans);
else query_Min(lson(node), l, mid, x, ans);
push_up(node);
}
void update(int node, int l, int r, int L, int R, int x){
if(L > R) return;
if(L <= l && r <= R){ lazy[node] += x; return;}
push_down(node);
int mid = (l + r) >> 1;
if(L <= mid) update(lson(node), l, mid, L, R, x);
if(R > mid) update(rson(node), mid + 1, r, L, R, x);
push_up(node);
}
int main(){
int n; scanf("%d", &n);
for(int i = 1; i < n; i++) sum[i + 1] = sum[i] + i;
build(1, 1, n);
for(int i = 1; i <= n; i++) scanf("%lld", &s[i]);
for(int i = n; i > 0; i--){
query_Min(1, 1, n, s[i], Ans[i]);
update(1, 1, n, Ans[i] + 1, n, Ans[i]);
}
for(int i = 1; i <= n; i++) printf("%d ", Ans[i]);
}
I - Polygons CodeForces - 1208G
题意: 给一个圆,找出k个正多边形,这些正多边形顶点在圆上,边数为[3,n]且互不相同。问最少的顶点数。
思路: 不如假定一个起点0。对于一个正m边形,顶点位置为1/m,2/m,……,(m-1)/m。其中只有与m互质的分子才会导致增加顶点,因为不互质的一定被m的因子选了。这样来看,一个正m边形会增加的顶点数就为phi(m)。答案即为phi(3)~phi(n)排序后的前k个数之和+1(起点)。注意没有两条边的多边形,须特判。(很显然,如果k>1,则一定会选到phi(4),即一定会有2的倍数,所以只需要特判k == 1)
代码:
#include <bits/stdc++.h>
#define ll long long
#define MAXN 1000010
using namespace std;
bool is_prime[MAXN + 10];
vector <int> prime;
int phi[MAXN + 10];
void seive(int n){
memset(is_prime, true, sizeof(is_prime));
is_prime[1] = false;
for(int i = 2; i <= n; i++){
if(is_prime[i]){ prime.push_back(i); phi[i] = i - 1;}
for(int j = 0; j < prime.size() && i * prime[j] <= n; j++){
is_prime[i * prime[j]] = false;
if(i % prime[j] == 0){ phi[i * prime[j]] = phi[i] * prime[j]; break;}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
int main(){
int n, k;
ll ans = 2;
scanf("%d%d", &n, &k);
seive(n);
sort(phi + 3, phi + n + 1);
if(k == 1) ans = 3;
else for(int i = 3; i < k + 3; i++) ans += phi[i];
printf("%lld\n", ans);
}
E - Tokitsukaze and Duel CodeForces - 1190C
这是一道没有套路的博弈论。
题意: 给一个长度为k的01串表示n张牌正面/背面朝上。两个玩家轮流按最优策略进行翻转操作,每次让连续k张牌同一面朝上。先让所有牌都同一面朝上的胜。问最后会是先手胜还是后手胜还是平局。
思路:
- 先手胜的情况:存在第一步就赢的走法
- 后手胜的情况:走第一步必输
- 平局:前两种请况都不满足。
- 如果第二步过后游戏还没结束,假设先手必败,那么他可以重复走第二步,这样先手就变后手了,所以不可能先手不可能败
- 如果先手必胜,那么后手一定第二步就重复第一步,那么先手后手又交换了,所以先手也不可能胜
- 那就只会是平局咯
第一步可以赢:判断很简单
第一步必输:慢慢想8
代码:
#include <bits/stdc++.h>
#define MAXN 100010
using namespace std;
int n, k;
char s[MAXN];
int cal(char tp, char op){
int ans = 0;
if(op == 'S') for(int i = 1; i <= n && s[i] == tp; ++i) ++ans;
else for(int i = n; i >= 1 && s[i] == tp; --i) ++ans;
return ans;
}
int judge(){
int white = 0, black = 0, ans;
s[1] == '0' ? (white += cal('0', 'S')) : (black += cal('1', 'S'));
s[n] == '0' ? (white += cal('0', 'E')) : (black += cal('1', 'E'));
if(max(white, black) + k >= n) return 1;
if(n <= 2 * k && black && white && min(white, black) + k > n - 2) return -1;
return 0;
};
int main(){
scanf("%d%d%s", &n, &k, s + 1);
int t = judge();
if(t == 1) printf("tokitsukaze\n");
else if(t == -1) printf("quailty\n");
else printf("once again\n");
}
D - Expected Square Beauty CodeForces - 1187F
这道题出得真好,考了概率论的相互独立和容斥,概念都很简单,但想不到构造相邻元素相同的概率就会很懵逼
详细题解:https://www.cnblogs.com/KingSann/articles/11118484.html
代码:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define MAXN 200010
using namespace std;
int qpow(int base, int n){
int ans = 1;
while(n){
if(n & 1) ans = 1ll * ans * base % MOD;
base = 1ll * base * base % MOD;
n >>= 1;
}
return ans;
}
int inv(int x){ return qpow(x % MOD, MOD - 2);}
int l[MAXN], r[MAXN], f[MAXN], p[MAXN], len[MAXN], Inv[MAXN];
int cal(int fir, int sec){
int L, R, t = fir - 1, pp;
L = max(l[t], max(l[fir], l[sec])), R = min(r[t], min(r[fir], r[sec]));
if(L > R) pp = 0;
else pp = 1ll * (R - L + 1) * Inv[t] % MOD * Inv[fir] % MOD * Inv[sec] % MOD;
return (1ll - p[fir] - p[sec] + pp + MOD) % MOD;
}
int main(){
int n, sum = 0, ans = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &l[i]);
for(int i = 1; i <= n; i++) scanf("%d", &r[i]);
for(int i = 1; i <= n; i++){
len[i] = r[i] - l[i] + 1;
Inv[i] = inv(len[i]);
}
f[1] = 1, Inv[0] = 1, l[0] = INF, r[0] = -1, p[1] = 0;
sum += f[1];
for(int i = 2; i <= n; i++){
int L = max(l[i], l[i - 1]), R = min(r[i], r[i - 1]);
if(L > R) p[i] = 0;
else p[i] = 1ll * (R - L + 1) * Inv[i] % MOD * Inv[i - 1] % MOD;
f[i] = (1 - p[i] + MOD) % MOD;
sum = (sum + f[i]) % MOD;
}
for(int i = 1; i <= n; i++){
ans = (ans + f[i]) % MOD;// i == j
int t = (sum - f[i] + MOD) % MOD;// |i - j| > 1
if(i > 1) t = (t - f[i - 1] + MOD) % MOD;
if(i < n) t = (t - f[i + 1] + MOD) % MOD;
ans = (ans + 1ll * f[i] * t % MOD) % MOD;
if(i > 1) ans = (ans + cal(i - 1, i)) % MOD;// i - j == 1
if(i < n) ans = (ans + cal(i, i + 1)) % MOD;
}
printf("%d\n", ans);
}
最后
F题是AC自动机啥的,字符串我不会a……
// |i - j| > 1
if(i > 1) t = (t - f[i - 1] + MOD) % MOD;
if(i < n) t = (t - f[i + 1] + MOD) % MOD;
ans = (ans + 1ll * f[i] * t % MOD) % MOD;
if(i > 1) ans = (ans + cal(i - 1, i)) % MOD;// i - j == 1
if(i < n) ans = (ans + cal(i, i + 1)) % MOD;
}
printf("%d\n", ans);
}
## 最后
F题是AC自动机啥的,字符串我不会a……
H是啥SoSdp……感觉很难的样子……改天补https://codeforces.com/blog/entry/45223