专栏文章
题解
个人记录参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miqri39r
- 此快照首次捕获于
- 2025/12/04 09:33 3 个月前
- 此快照最后确认于
- 2025/12/04 09:33 3 个月前
题意:
第一行输入两个数 , 表示有 个数, 对不等关系,接下来 行,每行两个数 , 表示
问最少还需要比较多少次才能确定一个数是第二大的数
数据范围:
思考:
什么数可能是第二大的数不太好找,那就考虑正难则反,求什么数不能成为第二大的数,剩下的自然是可能成为第二大的
显然的:当 并且存在 时, 显然不会是第二大的数,当然 不一定是题目直接给出的条件,可以是推理出来的,比如有 也可以证明 不是第二大的数
考虑时间复杂度,排除 和 ,看上去只有 和线性可以通过,明显线段树,二分答案一点用都没有,建图也不是一棵树,跑最短路也没什么用
等等,图?
输入 就连一条从 到 的有向边,用链式前向星存边,并且将 , 的值表明至少有几个数比 大
套路的,我们可以用 表示一个点有没有操作过,从 到 遍历,若 ,证明至少有一个数比 大,那么对于每一个 都有 那么就可以把所有这样的 赋为 ,并立刻操作
这样就能保证不重不漏地把每一个不可能成为第二大的数筛掉,这很好证明,读者可以自己尝试一下
无视掉已经被排除的点,得到这样一张图:

可以发现是很多棵一层树,也就是每个 连接了若干个
考虑只有一棵树,那么显然次数是 的个数减
不难想到,每次比较子节点最小的两棵子树一定不劣(可以用调整法证明),那么就 (已经做了一次比较了),然后把最小的两个树出队,然后入队比较大的那个数加

AC代码
CPP#include<bits/stdc++.h>
using namespace std;
int n,m,cnt,head[1000005],fa[1000005],bz[1000005],a[1000005],ans;
bool vis[1000005];
struct edge{
int to,nextt;
}edge[1000005];
priority_queue<int,vector<int>,greater<int>> q;
int in(){int k=0,f=1;char c=getchar();while (c<'0'||c>'9'){if (c=='-') f=-1;c=getchar();}while (c>='0'&&c<='9'){k=k*10+c-'0';c=getchar();}return k*f;}
void addedge()
{
int x=in(),y=in();
fa[y]=x;
bz[y]++;
cnt++;
edge[cnt].to=y;
edge[cnt].nextt=head[x];
head[x]=cnt;
}
void solve(int z)
{
vis[z]=true;
for (int i=head[z];i;i=edge[i].nextt)
{
bz[edge[i].to]=2;
if (!vis[edge[i].to])
{
solve(edge[i].to);
}
}
}
signed main()
{
n=in();m=in();
for (int i=1;i<=m;i++)
{
addedge();
}
for (int i=1;i<=n;i++)
{
if (!vis[i]&&bz[i]>0)
{
solve(i);
}
}
for (int i=1;i<=n;i++)
{
if (bz[i]==1)
{
a[fa[i]]++;
}
}
for (int i=1;i<=n;i++)
{
if (!bz[i])
{
ans++;
q.push(a[i]);
}
}
while (q.size()>1)
{
q.pop();
q.push(q.top()+1);
q.pop();
}
cout<<q.top()-1+ans-1;
return 0;
}
时间复杂度 常数极小,实测只需要200ms不到
当然可以用合并果子的思想优化到 ,不过本人比较懒就用 了
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...