2018.2.6 队列+次小生成树

2018.2.6 队列+次小生成树

标签: 考试


本次考试排名:Rank23
本次考试题目推荐:数字游戏(细节巨多),麦乐鸡翅(可后悔堆)
板子:次小生成树(数据较弱)
今天没有心路历程(Angry)
对自己算作一次警告


1.数字游戏

题面描述

认识到了游泳的巨大安全隐患之后,小朋友们都决定不去游泳了。于是,游泳馆冷清了下来。为了能因时制宜、因事制宜,游泳馆决定在游泳项目之外,还开设头脑风暴项目。头脑风暴当中有这样一道题目:给出一个LEN位的数字A,你可以删除数字A当中的N位,删除之后得到一个LEN-N位的数字B,你的目标就是使这个数字B最小。

数据范围

对于 50%的数据,LEN<=100000.
对于 100%的数据,LEN<=5000000 , 0<=N<=LEN.

解法

利用单调栈,从前往后尽力去维护递增,只要仍有删除次数就维护下去。说白了是贪心即可(每次删除左边第一个比右边大的)

好了,上代码

int Max=cnt-N;
for(int i=1;i<=cnt;i++)
{
    while(a[i]<zhan[top]&&N&&top)
        top--,N--;
    zhan[++top]=a[i];
}
bool flag=0;
for(int i=1;i<=top&&i<=Max;i++)
{
    if(!flag&&!zhan[i])continue;
    flag=1;
    printf("%d",zhan[i]);
}
if(!flag)puts("0");

2.麦乐鸡翅

题面描述

在太行山路上,有一家麦乐鸡翅的生意火爆。因为好吃,所以卖的特别好。排队的人就特别多,经常有很多人买不到鸡翅。鸡翅会在每分钟烤出 Xi X i 个,每分钟也只会卖给一个客人,第i个客人需要买 Yi Y i 个。因为生意火爆,老板可以选择在这分钟不卖给这个客人鸡翅,或者卖给这个顾客他需要的鸡翅,如果现在剩余的鸡翅不够,那就肯定不能卖给这个客人。无论这个客人能否买到鸡翅,他必须离开队伍。现在给定 N 分钟,且已经知道每分钟烤出的鸡翅个数 Xi X i ,也知道每个客人需要鸡翅的 Yi Y i 个数,现在老板想知道,如何合理安排卖给与拒绝,最多可以满足多少人

数据范围

50% 数据保证 N<=1000
100% 1<=N<=250000 Xi,Yi 都在[0,10^9]范围内

解法

弄一个大根堆出来,然后后悔就OK了,甚是简单(那你怎么不现切)

rg ll sum=0,now;
for(rg int i=1;i<=n;i++)
{
    sum+=a[i];
    if(!chi.empty())now=chi.top();
    if(sum>=b[i])
    {
        sum-=b[i];
        chi.push(b[i]);
    }
    else if(b[i]<now)
    {
        sum+=now-b[i];
        chi.pop();
        chi.push(b[i]);
    }
}
rg int ans=chi.size();
cout<<ans<<endl;

3.次小生成树

题面描述

小 C 最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。 正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e 的权值)

数据范围

数据中无向图无自环;
50% 的数据 N≤2 000 M≤3 000;
80% 的数据 N≤50 000 M≤100 000;
100% 的数据 N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

解法

最小生成树与次小生成树一定只有一条边的长度不同(证明感性思考一下就好了),因而我们要先构出一棵最小生成树,然后再从边集中选入某条边以此来代替最小生成树上的边。由于我们新加入的边所连接的两点x,y必定是在同一个集合中的了,因而我们考虑将x->y路径上某条边断掉,那么为了使修改后的边权和尽可能接近最小生成树的边权和,我们查询x->y路径上的最大值,因而这个可以用倍增或树剖+线段树完成(笔者用的是倍增)
但是!!!(这个题目这么简单就太天真了!)会存在如下情况
有多组最小生成树满足条件
因而我们还需要维护一个次大值,在有多组最大满足下输出次大就好了!这样是不是可以解决问题了!好了,还有一些细节要处理,笔者放在代码里了,希望对大家有帮助(这个题目要思想有思想,要难度有难度,要细节有细节)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define rg register
#define ll long long
#define inf 1e18
const int MAXN=3e5+5;
int N,M;
ll ans=inf,s;
inline int read()
{
    rg char ch='!';rg int z=1,num=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')z=-1,ch=getchar();
    while(ch<='9'&&ch>='0')num=(num<<3)+(num<<1)+ch-'0',ch=getchar();
    return z*num;
}
struct edge{int x,y,z,pd;}e[MAXN];
inline bool cmp(edge A,edge B)
{
    if(A.z!=B.z)return A.z<B.z;
    if(A.x!=B.x)return A.x<B.x;
    return A.y<B.y;
}
int bcj[MAXN];
int find(int x)
{
    if(bcj[x]!=x)bcj[x]=find(bcj[x]);
    return bcj[x];
}
struct hand{int to,next,w;}a[MAXN<<1];
int head[MAXN],cnt,tot;
inline void link(int u,int v,int z)
{
    a[++cnt]=(hand){v,head[u],z};
    head[u]=cnt;
}
int dep[MAXN],f[MAXN][26];
ll g1[MAXN][26],g2[MAXN][26];
void dfs(int u,int fa)
{
    for(rg int i=head[u];i;i=a[i].next)
    {
        rg int v=a[i].to;
        if(v==fa)continue;
        f[v][0]=u;
        g1[v][0]=a[i].w;
        g2[v][0]=-inf;
        dep[v]=dep[u]+1;
        dfs(v,u);
    }
}
inline void Kru()
{
    rg int cao=0;
    for(rg int i=1;i<=M;i++)
    {
        rg int x=e[i].x,y=e[i].y;
        rg int dx=find(x),dy=find(y);
        if(dx!=dy)
        {
            bcj[dy]=dx;
            e[i].pd=1;
            cao++;
            s+=e[i].z;
            link(y,x,e[i].z);
            link(x,y,e[i].z);
        }
        if(cao==N-1)break;
    }
}
inline void match(int &u,int j,ll &maxx,ll &semx)
{
    if(maxx>g1[u][j])
    {
        semx=max(semx,g1[u][j]);
    }
    else if(maxx<g1[u][j])
    {
        semx=max(maxx,g2[u][j]);
        maxx=g1[u][j];
    }
    else
    {
        semx=max(semx,g2[u][j]);
    }
    u=f[u][j];
}
inline ll Lca(int u,int v,int stand)
{
    if(dep[u]<dep[v])swap(u,v);
    rg int cha=dep[u]-dep[v];
    rg ll maxx=-inf,semx=-inf;
    for(rg int j=20;j>=0;j--)
    {
        if((1<<j)&cha)
            match(u,j,maxx,semx);
    }
    if(u==v)
    {
        if(maxx==stand){return semx;}
        return maxx;
    }
    for(rg int j=20;j>=0;j--)
    {
        if(f[u][j]!=f[v][j])
        {
            match(u,j,maxx,semx);
            match(v,j,maxx,semx);
        }
    }
    match(u,0,maxx,semx);match(v,0,maxx,semx);
    if(semx==-inf)return -inf;
    if(maxx==stand)return semx;
    return maxx;
}
int main()
{
    //freopen("pai.in","r",stdin);
    //freopen("a.out","w",stdout);
    N=read();M=read();
    for(rg int i=1;i<=M;i++)e[i]=(edge){read(),read(),read()};
    for(rg int i=1;i<=N;i++)bcj[i]=i;
    sort(e+1,e+M+1,cmp);
    for(rg int i=0;i<=20;i++)g1[1][i]=g2[1][i]=-inf;
    Kru();dep[1]=1;dfs(1,0);
    for(rg int j=1;j<=20;j++)
    {
        for(rg int i=1;i<=N;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            g1[i][j]=max(g1[i][j-1],g1[f[i][j-1]][j-1]);
            if(g1[i][j-1]!=g1[f[i][j-1]][j-1])
            {
                g2[i][j]=min(g1[i][j-1],g1[f[i][j-1]][j-1]);
            }
            else
            {
                g2[i][j]=max(g2[i][j-1],g2[f[i][j-1]][j-1]);
            }
        }
    }
    rg int x,y;
    for(rg int i=1;i<=M;i++)
    {
        if(e[i].pd)continue;
        x=e[i].x,y=e[i].y;
        ans=min(ans,s-Lca(x,y,e[i].z)+e[i].z);
    }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值