A.Valid BFS?
题意
给出一棵树和一个序列,求这个序列是不是从1号进行的BFS序
思路
由于n过大,用map存储两点是否存在边。直接模拟队列,并且实时记录队头结点被记录的次数,若达到该点连接的边数,就将此点移出队列。
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-23 20:03:43
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-27 15:46:08
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.23\A.cpp
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, a[N], cnt[N], cnt1[N];
map<int, map<int, int>> m;
int main()
{
cin >> n;
memset(cnt, 0, sizeof(cnt));
memset(cnt1, 0, sizeof(cnt1));
for (int i = 1, x, y; i < n; i++) {
cin >> x >> y;
m[x][y] = m[y][x] = 1;
cnt[x]++;
cnt[y]++;
}
for (int i = 1; i <= n; i++)
cin >> a[i];
// memset(vis, 0, sizeof(vis));
queue<int> q;
q.push(1);
if (a[1] != 1) {
printf("No\n");
return 0;
}
for (int i = 2; i <= n; i++) {
q.push(a[i]);
int t = q.front();
while (cnt1[t] == cnt[t]) {
q.pop();
t = q.front();
}
if (m[t][a[i]])
cnt1[t]++, cnt1[a[i]]++;
else {
printf("No\n");
// printf("%d\n", i);
return 0;
}
}
printf("Yes\n");
return 0;
}
B.Connected Component on a Chessboard
题意
现有一个国际象棋棋盘,已知(1,1)是白色。现在让你找一个连通块满足该连通块中有b个黑块和w个白块。
思路
一字长蛇显然是最优的,若b<w,则只要 w ≤ 3 × b + 1 w\le 3\times b+1 w≤3×b+1就可以构造出来;反之亦然
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-23 21:17:15
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-24 13:33:45
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.23\B.cpp
*/
#include <bits/stdc++.h>
using namespace std;
int n;
int main()
{
cin >> n;
for (int i = 1, x, y; i <= n; i++) {
cin >> x >> y;
int f = 1, cnt = 0, j;
if (x > y)
swap(x, y), f = 0;
if (y > 3 * x + 1) {
printf("NO\n");
continue;
}
printf("YES\n");
for (j = 2; cnt < x; j += 2)
printf("2 %d\n", j + f), cnt++;
j -= 2;
cnt = 0;
for (int k = 3; k < j && cnt < y; k += 2)
printf("2 %d\n", k + f), cnt++;
for (int k = 2; k <= j && cnt < y; k += 2)
printf("1 %d\n", k + f), cnt++;
for (int k = 2; k <= j && cnt < y; k += 2)
printf("3 %d\n", k + f), cnt++;
if (cnt < y)
printf("2 %d\n", 1 + f), cnt++;
if (cnt < y)
printf("2 %d\n", j + 1 + f);
}
return 0;
}
C.White Lines
题意
现在有 n × n n\times n n×n和块,每个块可以是黑色或白色。现在有一个板擦,可以将 k × k k\times k k×k个块变白。求板擦擦完后,最多有几行几列全部都是白色。
思路
显然染白后不会影响原先的答案,于是我们可以先预处理原先的白线数
对于每个染白的方案,我们先预处理,然后可以O(1)计算 出他的贡献
可以先计算出前缀和
a
[
i
]
[
j
]
a[i][j]
a[i][j]表示第j列行上的前缀和
b
[
i
]
[
j
]
b[i][j]
b[i][j]表示第i行列上的前缀和
x
[
i
]
[
j
]
x[i][j]
x[i][j]表示去除
[
i
−
k
+
1
i
]
[
j
]
[i-k+1~i][j]
[i−k+1 i][j]后对答案的贡献
y
[
i
]
[
j
]
y[i][j]
y[i][j]表示去除
[
i
]
[
j
−
k
+
1
j
]
[i][j-k+1~j]
[i][j−k+1 j]后对答案的贡献
只有在以下条件同时成立的时候,在i,j处才会对
x
[
i
]
[
j
]
x[i][j]
x[i][j]有贡献:
a
[
i
−
k
]
[
j
]
=
=
0
a[i-k][j]==0
a[i−k][j]==0
a
[
n
]
[
j
]
−
a
[
i
]
[
j
]
=
=
0
a[n][j]-a[i][j]==0
a[n][j]−a[i][j]==0
a
[
i
]
[
j
]
−
a
[
i
−
k
]
[
j
]
>
0
a[i][j]-a[i-k][j]>0
a[i][j]−a[i−k][j]>0
即1~i-k+1,i~n必须全都是0
第三个条件是避免重复计算(因为如果原来就是空的那么已经算在答案中了)
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-24 13:43:38
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-27 12:33:54
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.23\C.cpp
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5;
int n, k;
int a[N][N], b[N][N], x[N][N], y[N][N], ans = 0, tot = 0;
int main()
{
char c;
cin >> n >> k;
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> c;
while (c != 'B' && c != 'W')
cin >> c;
a[i][j] = a[i - 1][j] + (c == 'B');
b[i][j] = b[i][j - 1] + (c == 'B');
}
}
for (int i = 1; i <= n; i++) {
tot += a[n][i] == 0;
tot += b[i][n] == 0;
}
for (int i = k; i <= n; i++) {
for (int j = 1; j <= n; j++)
x[i][j] = x[i][j - 1] + (a[i - k][j] == 0 && a[n][j] - a[i][j] == 0 && a[i][j] - a[i - k][j] > 0);
}
for (int j = k; j <= n; j++) {
for (int i = 1; i <= n; i++)
y[i][j] = y[i - 1][j] + (b[i][j - k] == 0 && b[i][n] - b[i][j] == 0 && b[i][j] - b[i][j - k] > 0);
}
for (int i = k; i <= n; i++)
for (int j = k; j <= n; j++)
ans = max(ans, x[i][j] - x[i][j - k] + y[i][j] - y[i - k][j]);
cout << tot + ans << endl;
return 0;
}
D.Subway Pursuit
题意
现在有一个数字让你去猜,你可以问是否在一个区间中,但每次猜完之后,它会向左或向右移动0~k个位置,要求在4500次以内猜到这个数字
思路
二分大概找到这个数字的范围,比如缩到50左右的时候开始猜。然后没猜中之后,将区间向左向右各放大k个即可进行下次二分。
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-27 12:40:07
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-27 12:48:43
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.23\D.cpp
*/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
char a[10];
bool query(ll l, ll r)
{
cout << l << ' ' << r << endl;
fflush(stdout);
scanf("%s", a);
if (a[0] == 'Y')
return 1;
return 0;
}
ll n, l, r;
int k;
int main()
{
srand(time(0));
cin >> n >> k;
l = 1;
r = n;
while (1) {
while (r - l > 50) {
ll mid = (l + r) / 2;
if (query(l, mid))
r = mid;
else
l = mid + 1;
l = max(1ll, l - k);
r = min(n, r + k);
}
ll p = rand() % (r - l + 1) + l;
if (query(p, p))
return 0;
l = max(1ll, l - k);
r = min(n, r + k);
}
return 0;
}
E.Network Safety
题意
一个图是安全的是指边上的两点的点权不同。现有一个安全的图,可以向它进行操作:对某些点进行将它的点权与x异或,进行操作后还是安全的图。现在问有多少种不同的方案。
思路
x的范围为
0
0
0~
2
k
−
1
2^{k}-1
2k−1,图有n个点,那么总共的方案数为
2
k
+
n
2^{k+n}
2k+n种(包括不合法的方案)
设大写字母为点,小写字母为点权
如果现在选择A,B两点,那么A,B两点无论怎么进行异或,都不会破坏图的安全性,因为原本的图是安全的,a,b不同,异或同一个数后也各不相同
那么现在破坏图的安全性只有一种可能性,我选了A,没选B,而
a
⨁
x
=
b
a\bigoplus x=b
a⨁x=b
对于一条边,我们知道了要让图不安全就是只选两端点中的一个且两点异或值为x
那么当
x
=
a
⨁
b
x=a\bigoplus b
x=a⨁b时,必须同时选择AB两点,所以可以把它们当成一点
我们合并k个点后,这k点之间的异或值都不等于x,我们可以有
2
k
2^k
2k个选法
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-27 12:55:18
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-27 13:23:29
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.23\E.cpp
*/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
const int N = 5e5 + 5;
int n, m, k;
ll b[N];
ll ans;
map<ll, int> a;
map<ll, int> g[N];
ll quickmod(ll x, ll y)
{
ll ret = 1;
while (y) {
if (y & 1)
ret = ret * x % mod;
x = x * x % mod;
y /= 2;
}
return ret;
}
int find(int x, ll key)
{
if (g[x][key] == x)
return x;
return g[x][key] = find(g[x][key], key);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
ans = quickmod(2, k + n);
for (int i = 1; i <= n; i++)
cin >> b[i];
for (int i = 1, x, y; i <= m; i++) {
cin >> x >> y;
ll key = b[x] ^ b[y];
if (!g[x][key])
g[x][key] = x;
if (!g[y][key])
g[y][key] = y;
int fx = find(x, key);
int fy = find(y, key);
if (fx == fy)
continue;
else
g[fx][key] = g[fy][key];
// ans = ((ans - quickmod(2, n - a[key])) % mod + mod) % mod;
a[key]++;
ans = (ans - quickmod(2, n - a[key]) + mod) % mod;
}
cout << ans << endl;
return 0;
}