A. 美丽的路径(二分 + 搜索)
可以用并查集判联通性,无法到达直接NO。
YES的话考虑二分答案,权值 >= mid 的点赋为1,权值 < mid 的点赋为0
(1)从s开始搜索,如果有两个相邻的点都 > mid,那么check一定返回1,因为可以来回走这两个点
(2)在上一条不满足的前提下,有效路径只能是010101或101010,起点和终点不能都是0
(3)从s开始搜索,找是否有一条01交替的路径可以到达 t
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 7;
const int M = 4e5 + 7;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-')
f = -1;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
bool vis[N], flag;
int n, m, s, t, a[N], head[N], tot, fa[N];
struct Edge {
int v, nxt;
} edge[M];
void init() {
tot = 0;
memset(head, -1, sizeof(head));
for(int i = 1; i <= n; ++i) fa[i] = i;
}
void addedge(int u, int v) {
edge[tot].v = v;
edge[tot].nxt = head[u];
head[u] = tot++;
}
int Find(int x) {
if(x == fa[x]) return x;
return fa[x] = Find(fa[x]);
}
void Union(int x, int y) {
int xx = Find(x);
int yy = Find(y);
if(xx != yy) fa[xx] = yy;
}
void dfs1(int u, int x) {
vis[u] = 1;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(a[u] >= x && a[v] >= x) {
flag = 1;
return ;
}
if(!vis[v]) dfs1(v, x);
}
}
void dfs2(int u, int x) {
vis[u] = 1;
if(u == t) return ;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(vis[v] || (a[u] >= x && a[v] >= x) || (a[u] < x && a[v] < x)) continue;
dfs2(v, x);
}
}
bool check(int x) {
flag = 0;
for(int i = 1; i <= n; ++i) vis[i] = 0;
dfs1(s, x);
if(flag) return 1;
if(a[s] < x && a[t] < x) return 0;
for(int i = 1; i <= n; ++i) vis[i] = 0;
dfs2(s, x);
return vis[t];
}
int lower_bound(int l, int r) {
while(l < r) {
int mid = (l + r + 1) >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
int _, u, v;
_ = read();
while(_--) {
n = read(), m = read(), s = read(), t = read();
init();
int minn = inf, maxx = 0;
for(int i = 1; i <= n; ++i) {
a[i] = read();
minn = min(minn, a[i]);
maxx = max(maxx, a[i]);
}
while(m--) {
u = read(), v = read();
addedge(u, v);
addedge(v, u);
Union(u, v);
}
if(Find(s) != Find(t)) {
printf("NO\n");
continue;
}
printf("YES\n%d\n", lower_bound(minn, maxx));
}
return 0;
}
B. 比武招亲(上)(思维 + 组合数)
单独计算 i 的贡献,
i 作为最大值时,从 i 种数里取m - 1个数有多少种
i 作为最小值时,从 n - i + 1 种数里取m - 1个数有多少种
(1)不放回,有序
(2)不放回,无序
(3)放回,有序
(4)放回,无序
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll mod =998244353;
const ll N = 2e6 + 7;
ll inv[N], fac[N];
ll qpow(ll a, ll b) {
ll ans = 1;
a %= mod;
while(b) {
if(b & 1)
ans = a * ans % mod;
a = a * a % mod;
b >>= 1;
}
return ans % mod;
}
void init() {
inv[0] = 1;
fac[0] = 1;
for(ll i = 1; i < N; ++i) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = qpow(fac[i], mod - 2);
}
}
ll com(ll a, ll b) {
return fac[a] * inv[b] % mod * inv[a - b] % mod;
}
int main() {
init();
ll n, m;
scanf("%lld%lld", &n, &m);
ll ans = 0;
for(ll i = 1; i <= n; ++i) {
ans = (ans + i * com(i + m - 2, m - 1) % mod) % mod;
ans = (ans - i * com(n - i + m - 1, m - 1) % mod + mod) % mod;
}
printf("%lld\n", ans);
}
D. 石子游戏(差分)
题解没看懂,贴一篇不错的解释过程https://blog.csdn.net/weixin_45509601/article/details/113951375
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;
ll a[N], b[N];
int main(){
int t, n, k;
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
b[i] = a[i] - a[i - 1];
}
ll ans = 0;
bool flag = 1;
for(int i = 2; i <= n; ++i) {
if(b[i] < 0) {
if(i + k > n + 1) {
flag = 0;
break;
}
ans -= b[i];
b[i + k] += b[i];
b[i] = 0;
}
else if(b[i] > 0) {
if((i - 1) % k) {
flag = 0;
break;
}
ans += (i - 1) / k * b[i];
b[i] = 0;
}
}
if(!flag) printf("-1\n");
else printf("%lld\n", ans);
}
return 0;
}
E. 树上博弈(状压dp)
数据大小摆明了要状压,定义 为状态 i 的最大值,i 中为1的二进制位表示结点已经被删除,为0表示未被删除。这里边的储存比较灵活,用
的第 j 个二进制位为1表示 i、j 之间有边。
PS:今天注意到一个事情,memset初始化为 -inf 时,数组中的值和 -inf 不相等,随即科普了一下memset的初始化原理:(摘自巨佬~)
因为char是1字节,memset是按照字节赋值的,相当于把每个字节都设为那个数,所以char型的数组可赋任意值,int是4个字节,当memset(,1,sizeof()); 1相当于ASSCII码的1,1转为二进制00000001,当做一字节,一字节8位,int为4字节,所以初始化完每个数为00000001000000010000000100000001 = 16843009 .
memset(,0xff,sizeof()),0xff转为二进制11111111,int为4字节所以最后为11111111111111111111111111111111为-1。(化为二进制补位,然后再赋值)。可以全赋值为0,0的二进制位000000000000000000000000000000000,还可以是-1,-1的二进制就是11111111111111111111111111111111,所以memset可以直接初始化(0,-1);
例如:0xff转为二进制位11111111,正好是一位,0x1f小于0xff,而0x59也小于0xff,所以这些都可以用来初始化,只要能填满8位的二进制,就可以了。
如果你想初始最大化,第一位为符号位,不能为1,剩下全是1,也就是7个1,1111111化为十六进制正好为0x7f,所以memset(,0x7f,sizeof());就可以了当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的初始化),就不能使用memset函数而得自己写循环了(写这些不重要的代码真的很痛苦),我们知道这是因为memset是按字节操作的,它能够对数组清零是因为0的每个字节都是0,现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))
所以说以后再初始化 -inf 就不要憨憨地把数组的值跟 -inf 比较了,判断a[i]是否是初始值直接跟数组被赋的初始值比较。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1100000;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
ll dp[N], a[25], e[25];
int main() {
int t, n, u, v;
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
for(int i = 0; i < N; ++i) dp[i] = -inf;
for(int i = 0; i <= n; ++i) e[i] = 0;
for(int i = 0; i < n; ++i) scanf("%lld", &a[i]);
for(int i = 0; i < n - 1; ++i) {
scanf("%d%d", &u, &v);
u--, v--;
e[u] += 1 << v;
e[v] += 1 << u;
}
for(int i = 0; i < n; ++i) dp[1 << i] = a[i];
int bit = 1 << n;
for(int i = 1; i < bit; ++i) {
if(dp[i] == -inf) continue;
for(int j = 0; j < n; ++j) {
int tmp = 1 << j;
if(!(i & tmp) && (i & e[j]))
dp[i | tmp] = max(dp[i | tmp], a[j] - dp[i]);
}
}
printf("%lld\n", dp[bit - 1]);
}
return 0;
}
F. 我的心是冰冰的(签到)
树没有环,最多只要两种颜色
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f;
const ll MAXN = 2e5 + 7;
const ll MAXM = 5e5 + 7;
int main() {
int t, n, u, v;
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
for(int i = 1; i < n; ++i) scanf("%d%d", &u, &v);
if(n == 1) printf("1\n");
else printf("2\n");
}
return 0;
}
G. 模仿游戏(贪心)
哥哥肯定是要优先打新怪的,如果某时刻哥哥不在打怪但是妹妹队列中还有怪,哥哥要帮妹妹打一个
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 7;
const ll M = 4e5 + 7;
const ll inf = 0x3f3f3f3f;
const ll mod = 998244353;
struct node {
ll id, x;
bool operator < (const node &a) const {
return id < a.id;
}
} s[N];
vector<ll>tim[N];
ll vis[N];
int main() {
ll t, n;
scanf("%lld", &t);
while(t--) {
scanf("%lld", &n);
for(ll i = 1; i <= n; ++i) vis[i] = 0;
for(ll i = 1; i < N; ++i) tim[i].clear();
for(ll i = 1; i <= n; ++i) {
scanf("%lld%lld", &s[i].id, &s[i].x);
tim[s[i].id].emplace_back(s[i].x);
}
sort(s + 1, s + n + 1);
ll q1 = 0, q2 = 0;
for(ll i = 1; i <= 200000; ++i) {
ll siz = tim[i].size();
for(ll j = 0; j < siz; ++j) {
if(vis[tim[i][j]] == 0) {
q1 = max(q1 + 1, i);
vis[tim[i][j]] = i;
}
else {
if(vis[tim[i][j]] == i) q2 = max(q2 + 1, i + 1);
else q2 = max(q2 + 1, i);
}
}
if(q1 < q2 && q1 < i) q1 = i, q2--;
}
printf("%lld\n", max(q1, q2));
}
return 0;
}