题解:CF1919D 01 Tree
Graph_Theory · · 题解
题意
有一种节点两边边权为
给定
说明一下,这里的满二叉树是指子节点数量为
思路
同一个父亲的两个叶子结点
当然这个很明显是错的。
考虑 0 1 2 这样一组样例,如果按照上面的思路,我们可以得到两种结果,第一种是将 0 1 合并得到 0 2不成立。第二种是将 1 2 合并得到 0 1 此时又成立。
所以我们发现,我们每一次合并时都应该从最大节点开始合并。
所以直接搞一个优先队列每次把一个节点丢进去,每一次选择
若一个点的左边和右边
若一个点的左边和右边
然后
然后发现 3 3 2 1 0 过不去,原来访问到第一个
还是过不去。发现是合并完之后 3 2 1 0 会默认向右合并,于是当两个点都可以合并时优先合并
还有就是当一个点非常大,两边都没法合并,也是无解的。
删除完之后把当前节点的右节点就连接在刚刚消除的那个点的右节点上,刚刚消除的那个点的右节点的左节点就连接到当前点上。
代码
#include <bits/stdc++.h>
#define maxn 200009
using namespace std;
int n;
//bool vis[maxn];
struct item
{
int id, dis, live = 1, l, r;
bool operator <(const item& b) const
{
return dis < b.dis;
}
bool operator >(const item& b) const
{
return dis > b.dis;
}
} a[maxn];
int cnt0;
struct que
{
int dis, id, rank, viscnt;
bool operator <(const que& b) const
{
return dis == b.dis ? rank > b.rank : dis <= b.dis;
}
bool operator >(const que& b) const
{
return dis == b.dis ? rank<b.rank: dis >= b.dis;
}
};
priority_queue<que> q;
int sum, num, ans, mergesum;
signed main_()
{
sum = num = ans = mergesum = cnt0 = 0;
for (int i = 1; i <= n; i++)
{
a[i].l = i - 1;
a[i].r = i + 1;
a[i].id = a[i].dis = 0;
a[i].live = 1;
}
while (!q.empty()) q.pop();
cin >> n;
mergesum = n;
for (int i = 1; i <= n; i++)
{
cin >> a[i].dis;
a[i].id = i;
a[i].l = i - 1;
a[i].r = i + 1;
q.push({a[i].dis, i, 1, 0});
if (a[i].dis == 0) cnt0++;
if (cnt0 > 1) return cout << "No" << endl, 0;
}
if (cnt0 == 0) return cout << "No" << endl, 0;
int now = 0;
//cerr<<q.size()<<endl;
while (!q.empty())
{
//cerr<<"run"<<endl;
now++;
auto top = q.top();
q.pop();
if (a[top.id].live == 0 || mergesum == 1) continue;
// cerr<<top.id<<" "<<top.dis<<" "<<top.viscnt<<" "<<top.rank<<endl;
if (a[top.id].dis - a[a[top.id].r].dis > 1 && a[top.id].dis - a[a[top.id].l].dis > 1) return cout << "No" << endl, 0;
else if (a[a[top.id].r].dis == a[top.id].dis && a[a[top.id].l].dis == a[top.id].dis)
{
if (top.viscnt == now - 1) return cout << "No" << endl, 0;
q.push({a[top.id].dis, top.id, top.rank + 1, now});
continue;
}
int flag = 3;
if (
((a[a[top.id].l].dis == a[top.id].dis + 1 || a[a[top.id].l].dis + 1 == a[top.id].dis) && a[top.id].l != 0)
&&
((a[a[top.id].r].dis + 1 == a[top.id].dis || a[a[top.id].r].dis == a[top.id].dis + 1) && a[top.id].r != n + 1)
)
{
if (a[a[top.id].l].dis < a[a[top.id].r].dis) flag = 1;
else flag = 0;
// cerr<<"2ok"<<endl;
}
if (
(flag == 3 && ((a[a[top.id].r].dis + 1 == a[top.id].dis || a[a[top.id].r].dis == a[top.id].dis + 1) && a[top.id].r != n + 1)) || flag == 1)
{
// cerr<<a[top.id].dis<<" "<<a[a[top.id].r].dis<<" "<<a[top.id].r<<" "<<min(a[a[top.id].r].dis,a[top.id].dis)<<endl;
a[a[top.id].r].live = 0;
a[top.id].dis = min(a[a[top.id].r].dis, a[top.id].dis);
a[a[a[top.id].r].r].l = top.id;
a[top.id].r = a[a[top.id].r].r;
//
// cerr<<a[top.id].dis<<endl;
q.push({a[top.id].dis, top.id, 1, now});
mergesum--;
// cerr<<"merge r"<<endl;
}
else if (
(flag == 3 && ((a[a[top.id].l].dis == a[top.id].dis + 1 || a[a[top.id].l].dis + 1 == a[top.id].dis) && a[top.id].l != 0)) || flag == 0)
{
a[a[top.id].l].live = 0;
a[top.id].dis = min(a[a[top.id].l].dis, a[top.id].dis);
a[a[a[top.id].l].l].r = top.id;
a[top.id].l = a[a[top.id].l].l;
q.push({a[top.id].dis, top.id, 1, now});
mergesum--;
// cerr<<"merge l"<<endl;
}
// cerr<<"end"<<endl;
}
// cerr<<endl<<endl<<endl;
for (int i = 1; i <= n; i++)
{
if (a[i].live == 1) sum++;
if (a[i].dis == 0) num++;
if (a[i].live == 1 && a[i].dis == 0) ans++;
// cerr<<i<<" "<<a[i].live<<" "<<a[i].dis<<endl;
}
// cerr<<sum<<" "<<num<<endl;
// cerr<<a[1].r<<endl;
// cerr<<a[1].dis<<endl;
if (ans == 1 && sum == 1) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
cin >> T;
while (T--)
main_();
return 0;
}
然后发现上面这一坨石山代码常数太大超时了。
小小优化一下,发现成立的情况下合并的次数恰好是
然后又超时了。
如果连续两次访问到了同一个节点同样优化掉。发现这个似乎和上面的
发现可以只向右边合并,然后初始化的 push 条件从无条件设置为能和右边合并再 push 进去。这样向右合并优化掉了,那种很大的两边都没法合并的就不会再进去了。然后又遇到 3 3 2 1 0 了,这里更新了一个点后,把当前这个点和左边这个点 push 进去,
这样就写完了。
#include <bits/stdc++.h>
using namespace std;
#define maxn 200005
struct item
{
int dis;
int l, r;
bool live;
} a[maxn];
struct que
{
int dis, i;
bool operator<(que const& o) const
{
return dis != o.dis ? dis < o.dis : i > o.i;
}
};
int main()
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i].dis;
a[i].live = true;
a[i].l = i - 1;
a[i].r = i + 1;
}
a[0].live = a[n + 1].live = false;
a[0].r = 1;
a[n + 1].l = n;
priority_queue<que> pq;
auto try_push = [&](int i)
{//lambda表达式,这样不用清空数组了
int j = a[i].r;
if (i >= 1 && j <= n && a[i].live && a[j].live
&& abs(a[i].dis - a[j].dis) == 1)
{
pq.push({ min(a[i].dis, a[j].dis), i });
}
};
for (int i = 1; i < n; i++)
try_push(i);
int merges = 0;
while (!pq.empty() && merges < n - 1)
{
auto top = pq.top();
pq.pop();
int i = top.i, j = a[i].r;
if (!(i >= 1 && j <= n && a[i].live && a[j].live
&& abs(a[i].dis - a[j].dis) == 1
&& min(a[i].dis, a[j].dis) == top.dis))
continue;
a[i].dis = top.dis;
a[j].live = false;
merges++;
int jj = a[j].r;
a[i].r = jj;
a[jj].l = i;
try_push(a[i].l);
try_push(i);
}
bool ok = false;
if (merges == n - 1)
for (int i = 1; i <= n; i++)
if (a[i].live)
{
ok = (a[i].dis == 0);
break;
}
cout << (ok ? "YES\n" : "NO\n");
}
return 0;
}