最大权独立集问题#
解题思路#
题目中给出 和 二进制下异或结果的个数为奇数代表之间有一条边.那我们可以假设一下,将数组中的数按照二进制下 的个数划分为奇数和偶数两个集合.
对于一个集合中两个数(个), (个), 其中两个数二进制下有个是同时出现的,那么这两个数异或后的结果就有个
通过可以发现无论, 同为奇数或偶数结果都为偶数,可以证明同一个集合内的任意两个数不会有边.
参考代码#
#include <bits/stdc++.h>
#define int long long
#define pii std::pair<int,int>
void solve() {
int n;
std::cin >> n;
int a = 0, b = 0;
for (int x, i = 0; i < n; ++i) {
std::cin >> x;
int ct = __builtin_popcount(x); //__builtin_popcount(x) 为计算x在二进制下1的个数
if (ct & 1) a += x;
else b += x;
}
std::cout << std::max(a, b) << std::endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr), std::cout.tie(nullptr);
int T = 1;
std::cin >> T;
while (T --) solve();
return 0;
}cpp那一天的回文字符串#
解题思路#
字符串中只能同奇偶性的下标的字母可交换,那么我们就需要根据字符串长度做分类讨论:
1.对于偶数长度的字符串,只有偶数下标与奇数下标的字母完全相等才能组成回文字符串.
2.对于奇数长度的字符串,只有不成对出现的个数才能组成回文字符串.
参考代码#
#include <bits/stdc++.h>
#define int long long
#define pii std::pair<int,int>
void solve() {
std::string s;
std::cin >> s;
std::vector<int> a(30, 0), b(30, 0);
for (int i = 0; i < s.size(); i ++) {
if (i & 1) b[s[i] - 'a'] ++;
else a[s[i] - 'a'] ++;
}
bool res = false;
if (s.size() & 1) {
int t1 = 0, t2 = 0;
for (int i = 0; i < 26; i ++) {
t1 += (a[i] & 1);
t2 += (b[i] & 1);
}
res = (t1 + t2 <= 1);
} else {
res = (a == b);
}
std::cout << (res ? "YES" : "NO") << std::endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr), std::cout.tie(nullptr);
int T = 1;
std::cin >> T;
while (T --) solve();
return 0;
}cpp禁忌教典的消失咒文#
解题思路#
这道题的核心在于将原序列的交替符号特性吸收到前缀和中,然后通过分析删除区间长度奇偶性对剩余序列能量值的影响,把问题转化为对前缀和的等式计数。
记前缀和 。
删除区间 ,剩余部分由 和 拼接而成。前半部分能量为 。 后半部分的元素在原序列中位置前移了 位。由于符号与位置的奇偶相关,移动 位会导致后半部分每个元素的符号整体乘上 。因此后半部分能量为 。 剩余序列总能量: 要求 。
-
若区间长度为偶数( 为偶,即 与 同奇偶):
-
若区间长度为奇数( 为奇,即 与 不同奇偶):
设 ,则 。对于确定的右端点 ,需要找满足等式的 。利用 与 的奇偶关系,等式化为:
- 同奇偶时: (记为 )
- 不同奇偶时: (记为 )
- 不同奇偶时: (记为 )
我们可以遍历右端点 ,用两个哈希表分别维护已遍历的、下标为偶数的 和下标为奇数的 的出现次数。对于当前 :
- 若 为奇数:同奇偶的 必为奇数,查奇数表找 ;不同奇偶的 为偶数,查偶数表找 。
- 若 为偶数:同奇偶的 为偶数,查偶数表找 ;不同奇偶的 为奇数,查奇数表找 。
查询后,再将 按 的奇偶性存入对应的哈希表(这样保证只匹配 )。最后累加结果。
初始时 (对应 )是偶数且 ,因此将 ct0[0] 初始化为 1。
参考代码#
#include <bits/stdc++.h>
#define int long long
#define pii std::pair<int,int>
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n + 1, 0);
for (int i = 1; i <= n; i ++) {
std::cin >> a[i];
if (i % 2 == 0) a[i] = -a[i];
}
for (int i = 1; i <= n; i ++) a[i] += a[i - 1];
std::unordered_map<int, int> ct0, ct1;
int ans = 0;
ct0[0] ++;
for (int i = 1; i <= n; i ++) {
int v1 = k + a[i] - a[n];
int v2 = k - a[i] + a[n];
if (i & 1) {
ans += ct0[v2] + ct1[v1];
} else {
ans += ct0[v1] + ct1[v2];
}
if (i & 1) ct1[a[i]] ++;
else ct0[a[i]] ++;
}
std::cout << ans << std::endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr), std::cout.tie(nullptr);
int T = 1;
std::cin >> T;
while (T --) solve();
return 0;
}cpp永恒的奥古斯都#
解题思路#
题目要求:给定最终树 T' 每个点的颜色 (0/1)和树的结构,问有多少种可能的初始颜色状态 ,使得可以通过若干次“选择颜色为 0 的节点并翻转其子树”的操作变成 c'。
设变量 表示节点 u 是否被操作(奇偶性)。最终颜色与初始颜色、操作的关系为:
( v 是 u 的祖先)
已知 c',计数初始 c 等价于计数合法的操作变量 x(两者一一对应)。合法指存在一个操作顺序,使得每次操作时该节点当前颜色为 0。
可以自底向上进行 DP,维护以 u 为根的子树在两种情形下的合法初始状态数:
dp0[u]:u不操作 () 时子树内的方案数(实际上对于根,它包含了所有合法情况的答案)。dp1[u]:u操作 () 时子树内的方案数。
核心结论:若 u 被操作,则其子树内所有初始颜色都可以通过合法操作达到目标,方案数达到最大,即
其中 size(u) 是以 u 为根的子树大小。该值与 c' 无关,可以通过归纳证明。
对 u 的子节点 v,设 ,(代码中的记号)。
dp1[u]:无论c'_u为何,dp1[u] = 2 * v2。递归展开即 。dp0[u]:根据c'_u分情况:- 若 :
u必须不操作(否则最终颜色会变成 1),故dp0[u] = v1。 - 若 :
u可以不操作或操作。
不操作时贡献 v1;操作时贡献 v2(即 )。
因此 dp0[u] = v1 + v2。
根节点 1 没有父节点限制,所有合法方案均已包含在 dp0[1] 中,直接输出即可。
参考代码#
#include <bits/stdc++.h>
#define int long long
#define pii std::pair<int,int>
const int MOD = 998244353;
void solve() {
int n;
std::cin >> n;
std::vector<int> c(n + 1, 0);
std::vector g(n + 1, std::vector<int>());
for (int i = 1; i <= n; i ++) std::cin >> c[i];
for (int i = 1, u, v; i < n; i ++) {
std::cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
std::vector<int> fa(n + 1, 0), vis(n + 1, 0), b;
std::stack<int> stk;
stk.push(1);
fa[1] = 0;
while (!stk.empty()) {
auto u = stk.top();
if (!vis[u]) {
vis[u] = 1;
for (auto v: g[u]) {
if (v != fa[u]) {
fa[v] = u;
stk.push(v);
}
}
} else {
stk.pop();
b.push_back(u);
}
}
std::vector<int> dp0(n + 1, 0), dp1(n + 1, 0);
for (auto u: b) {
int v1 = 1, v2 = 1;
for (auto v: g[u]) {
if (v == fa[u]) continue;
v1 = dp0[v] * v1 % MOD, v2 = dp1[v] * v2 % MOD;
}
dp0[u] = (v1 + c[u] * v2) % MOD;
dp1[u] = (2 * v2) % MOD;
}
std::cout << dp0[1] % MOD << std::endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr), std::cout.tie(nullptr);
int T = 1;
// std::cin >> T;
while (T --) solve();
return 0;
}cpp