在很久很久以前,小张找到了一颗有 N 个节点的有根树 T 。 树上的节点编号在 1 到 N 范围内。 他很快发现树上的每个节点 i 都有一个对应的整数值 V[i]。 一个老爷爷对他说,给你一个整数 X, 如果你能回答我的 M 个问题,他就给张浩扬购买一些零食。 对于每个问题 Q[i], 请你找到 在 T 中以节点 Q[i] 为根的子树中的所有节点(包括 Q[i])中, 有没有两个节点 A, B (A != B) 的值 V[A] ^ V[B] 的异或和为 X。 如果有这样的两个节点, 请你输出 YES。 否则你需要输出 NO 表示没有节点符合上面的条件。
关于启发式合并,可以参考: OI Wiki 启发式合并
对于每个子树,以大小最大的子树为基础,每次将根节点或其它较小子树合并进去。
子树 YES,必然有当前树 YES。
不太确定复杂度怎么算,以下不一定对……
大概是考虑所有值被加入集合(哈希表)的总次数,每次O(1)。
对于任意一个节点,它的重子树不需要再次被加入哈希表,其他子树需要再次加入哈希表,最坏情况子树节点数一样大,累计O((子树个数-1)*单个子树节点数)
=O(总节点数-单个子树节点数)
,全加起来是O(总结点数+总结点数*(1-1/子树个数)+总结点数*(1-1/子树个数)^2+...)
= O(总结点数*[1+(1-1/子树个数)+(1-1/子树个数)^2+...])
。
假设树有h层,每层k个分支,则有1+k+k^2+...+k^(h-1) = n = (k^h-1)/(k-1) < k^h
。
O(n*[1+((k-1)/k)+((k-1)/k)^2+...+((k-1)/k)^(h-1)])
= O(n*[k^h-(k-1)^h]/[k^(h-1)])
= O(n)
#include
using namespace std;
const int N = 101000;
int n, x, m;
vector<int> e[N];
int value[N];
int sz[N], son[N];
void dfs(int u) {
sz[u] = 1;
for (const auto &v: e[u]) {
dfs(v);
sz[u] += sz[v];
if (son[u] == 0 || sz[son[u]] < sz[v]) {
son[u] = v;
}
}
}
bool ans[N];
unordered_set<int> work(int u) {
if (!son[u]) {
return {value[u]};
}
auto now = work(son[u]);
if (ans[son[u]]) {
ans[u] = true;
}
if (now.count(value[u] ^ x)) {
ans[u] = true;
}
now.insert(value[u]);
unordered_set<int> tmp;
for (const auto &v: e[u]) {
if (v == son[u])continue;
tmp = work(v);
if (ans[v]) {
ans[u] = true;
}
if (!ans[u]) {
for (const auto &it: tmp) {
if (now.count(it ^ x)) {
ans[u] = true;
break;
}
now.insert(it);
}
}
}
return now;
}
int main() {
cin >> n >> x >> m;
int rt = -1;
for (int i = 1, fa; i <= n; i++) {
scanf("%d", &fa);
if (fa == -1) {
rt = i;
} else {
e[fa].push_back(i);
}
}
for (int i = 1; i <= n; i++)scanf("%d", &value[i]);
dfs(rt);
work(rt);
int q;
for (int i = 1; i <= m; i++) {
scanf("%d", &q);
puts(ans[q] ? "YES" : "NO");
}
return 0;
}
树中两个节点的值异或为x,导致包含它们的最近公共祖先的子树返回YES。
所以先标记最近公共祖先,再dfs2标记最近公共祖先直到根节点。
但是如果整个树的节点值只有两种,时间复杂度O(n^2)
#include
using namespace std;
const int N = 101000;
int n, x, m;
vector<int> e[N];
int dep[N], f[N][20];
unordered_map<int, vector<int>> mp;
bool ans[N];
void dfs(int u) {
for (int i = 1; i < 20; i++) {
f[u][i] = f[f[u][i - 1]][i - 1];
}
for (const auto &v: e[u]) {
dep[v] = dep[u] + 1;
f[v][0] = u;
dfs(v);
}
}
int getLCA(int u, int v) {
if (dep[u] < dep[v]) {
swap(u, v);
}
for (int i = 19; i >= 0; i--) {
if (dep[f[u][i]] >= dep[v]) {
u = f[u][i];
}
}
if (u == v) {
return u;
}
for (int i = 19; i >= 0; i--) {
if (f[u][i] != f[v][i]) {
u = f[u][i];
v = f[v][i];
}
}
return f[u][0];
}
void dfs2(int u) {
for (const auto &v: e[u]) {
dfs2(v);
ans[u] |= ans[v];
}
}
int main() {
cin >> n >> x >> m;
int rt = -1;
for (int i = 1, fa; i <= n; i++) {
scanf("%d", &fa);
if (fa == -1) {
rt = i;
} else {
e[fa].push_back(i);
}
}
dep[rt] = 1;
dfs(rt);
for (int i = 1, value; i <= n; i++) {
scanf("%d", &value);
const auto &vList = mp[value ^ x];
for (const auto &v: vList) {
ans[getLCA(i, v)] = true;
}
mp[value].push_back(i);
}
dfs2(rt);
int q;
for (int i = 1; i <= m; i++) {
scanf("%d", &q);
puts(ans[q] ? "YES" : "NO");
}
return 0;
}
/*不是比赛时的用例
5 3 5
-1 1 1 3 3
1 2 1 2 1
1 2 3 4 5
*/
公交上有N排凳子,每排有两个凳子,每一排的凳子宽度不一样。有一些内向和外向的人按照顺序上车。 外向的人(0):只会选择没人的一排坐下,如果有很多排符合要求,他会选择座位宽度最小的坐下。 内向的人(1):只会选择有人的一排坐下,如果有很多排符合要求,他会选择座位宽度最大的坐下。 数据保证存在合理。输出每个人所在的排。
题面应该是把内向外向说反了,明明内向才自己坐……
间接排序得到大小序号。
外向找没人的坐,会按顺序坐,坐完后内向的就可以坐了,所以将大小序号入堆。
内向找有人的最大的坐,也就是将堆顶弹出。O( nlogn )
#include
using namespace std;
const int N = 101000;
int n;
int len[N], r[N];
char s[N * 2];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%d", &len[i]);
r[i] = i;
}
sort(r + 1, r + n + 1, [](int a, int b) {
return len[a] < len[b];
});
//for(int i=1;i<=n;i++){
// cout<
// }
scanf("%s", s + 1);
int pos = 0;
priority_queue<int> q;
for (int i = 1; i <= n * 2; i++) {
if (s[i] == '0') {
++pos;
printf("%d ", r[pos]);
q.push(pos);
} else {
int now = q.top();
q.pop();
printf("%d ", r[now]);
}
}
return 0;
}
一鼓作气再而衰三而竭。 小艺总是喜欢把任务分开做。 小艺接到一个任务,任务的总任务量是n。 第一天小艺能完成x份任务。 第二天能完成x/k。 。。。 第t天能完成x/(k^(t-1))。 小艺想知道自己第一天至少完成多少才能完成最后的任务。
印象中见过完全一样的题……
份数是整数,相当于每次取整。
第1天x,可以表示为x=a1+a2k+…+ank^(n-1) (0<=ai
…
第n天an
总数a1+a2*(k+1)+a3*(k ^2+k+1)+…+an*(k ^(n-1)+…+1) >= n
可以对n取模,每次模(k ^(n-1)+…+1) (k ^(n-2)+…+1) … (k+1) (1)
可以求出an…a1
代入x=a1+a2k+…+ank^(n-1)
可以求出答案。
#include
#include
#include
#include
using namespace std;
using LL = long long;
const int N = 1010;
LL pk[N];
LL sum[N];
int main() {
LL n, k;
cin >> n >> k;
pk[0] = 1;
sum[0] = pk[0];
for (int i = 1; i < N; i++) {
pk[i] = pk[i - 1] * k;
sum[i] = sum[i - 1] + pk[i];
}
int pos = 0;
for (; n / sum[pos] >= k; pos++) {
}
LL ans = 0;
for (int i = pos; i >= 0; i--) {
ans += n / sum[i] * pk[i];
n %= sum[i];
}
cout << ans << endl;
return 0;
}
N个节点两两建边。 不存在3个节点相互之前全部相连。(3个节点连接成环) 最多能建立多少条边?
二分图性质。
一开始画来画去想了半天
#include
#include
#include
#include
using namespace std;
using LL = long long;
int main() {
LL n;
cin >> n;
cout << (n / 2) * ((n + 1) / 2) << endl;
return 0;
}