17.4.3 图论专题 校内赛(Bricks)(Drive)(Graph)(Airplane)

这一回我们的校内赛由敬爱的jyb学长出题,主题是图论。下面来看题:

Problem 1. bricks


【解题报告】
这道题考察了并查集的灵活运用。因为他的修改和询问都很特殊,修改只给你一块砖的序号(你需要知道他所在的那一堆),询问又问你某一块砖以下的砖的个数。由于不告诉我们堆的序号,所以需要搞这三个数组:

father[i]//表示第i块砖所在的那一堆砖的最顶端的序号 
up[i]//表示第i块砖以上的砖的个数 
down[i]//表示第i块砖以下的砖的个数

除了并查集的合并需要更新这三个数组以外,我们在getfather时也会顺势更新这一路径上点的点。
下面我们来看代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define NAME "bricks"
using namespace std;
const int N=200000;
int father[N+5],down[N+5],up[N+5];//up表示以上,down表示以下 
int n,m;
int getfather(int x)//查询时顺势更新 
{
    int temp;
    if(x!=father[x])
    {
        temp=father[x];
        father[x]=getfather(father[x]);
        up[x]+=up[temp];
    }
    return father[x];
}
void _union(int x,int y)//并查集合并,x在上,y在下 
{
    x=getfather(x),y=getfather(y);
    if(x!=y)
    {
        father[y]=x;
        up[y]+=down[x];
        down[x]+=down[y];
    }
}
int main()
{
    freopen(NAME".in","r",stdin);
    freopen(NAME".out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    father[i]=i,down[i]=1,up[i]=0;//赋初值,down包括自己,up不包括自己 
    while(m--)
    {
        char s[10];
        scanf("%s",s);
        if(s[0]=='M')
        {
            int u,v;
            scanf("%d%d",&u,&v);
            _union(u,v);
        }
        else
        {
            int x;
            scanf("%d",&x);
            printf("%d\n",down[getfather(x)]-up[x]-1);//最后输出时把定点的down与这一点的up相减 
        }
    }
    return 0;
}

代码如下:

Problem 2. drive


【解题报告】
这道题一看就给人一种图论最短路的感觉,我们毫不犹豫地选择了时间复杂度最优的SPFA。具体思路就是先找到一条最短路劲,在一个一个枚举这条路上的每条边降雨的情况,再重新SPFA就完了。
需要注意的是,建图的时候要建单向边
代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define NAME "drive"
using namespace std;
const int M=10000;
const int N=4000;
int state[N+5],flag[N+5],pre[N+5];
double dis[N+5];
int head[N+5],num;
int n,m;
double tot=-1;
struct edge
{
    int u,v,next;//多存储一维u,来记录路的起点 
    double w;
    edge(){next=-1; }
};
edge ed[2*M+5]; 
void build(int u,int v,double w)
{
    ed[++num].v=v;
    ed[num].u=u;
    ed[num].w=w;
    ed[num].next=head[u];
    head[u]=num;
}
double SPFA(bool first)
{
    memset(flag,0,sizeof(flag));
    for(int i=2;i<=n;i++)
    dis[i]=1000000.0;
    int front=0,rear=1;
    dis[1]=0;
    state[rear]=1;
    flag[1]=1;
    do
    {
        front++;
        front=(front+1)%(N+5)-1;
        int u=state[front];
        flag[u]=0;
        for(int i=head[u];i!=-1;i=ed[i].next)
        {
            int v=ed[i].v;
            if(dis[v]>dis[u]+ed[i].w)
            {
                dis[v]=dis[u]+ed[i].w;
                if(first)pre[v]=i;//记录最短路径
                if(!flag[v])
                {
                    ++rear;
                    rear=(rear+1)%(N+5)-1;
                    state[rear]=v;
                    flag[v]=1;
                }
            }
        }
    }while(front!=rear);
    return dis[n];
}
int main()
{
    freopen(NAME".in","r",stdin);
    freopen(NAME".out","w",stdout);
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        double l,s;
        scanf("%d%d%lf%lf",&u,&v,&s,&l);
        build(u,v,l/s);//注意是单向边 
    }
    int now=n;
    double ans=SPFA(true);
    while(now!=1)//从后往前修改点权 
    {
        ed[pre[now]].w*=4.0;
        ans=max(ans,SPFA(false));
        ed[pre[now]].w/=4.0;
        now=ed[pre[now]].u;
    }
    printf("%.4lf",ans);
    return 0;
}
Problem 3. graph


【解题报告】

Problem 4. airplane


【解题报告】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值