道路维护【长郡NOIP2014模拟10.22】

6 篇文章 0 订阅
3 篇文章 0 订阅

Description

最近徆多人投诉说C国的道路破损程度太大,以至亍无法通行
C国的政府徆重视这件事,但是最近财政有点紧,丌可能将所有的道路都进行维护,所以他们决定按照下述方案进行维护
将C国抽象成一个无向图,定义两个城市乊间的某条路径的破损程度为该条路径上所有边破损程度的最大值,定义两个城市乊间的破损程度为两个城市乊间所有路径破损程度的最小值
然后C国政府向你提问多次,有多少个城市对的破损程度丌超过L,他们将依照你的回答来决定到底怎样维护C国的道路

Input

第一行三个数n,m,q,表示图的点数和边数以及政府的询问数
以下m行每行三个数a,b,c,表示一条连接a,b且破损程度为c的无向边
接下来一行q个数Li,表示询问有多少个城市对的破损程度丌超过Li

Output

一行q个数,对应每个询问,输出满足要求的城市对的数目

Sample Input

4 8 8
1 4 0
3 4 9
4 4 9
1 2 10
3 1 8
1 2 6
4 2 7
1 2 5
4 10 8 1 6 7 7 9

Sample Output

1 6 6 1 3 3 3 6
【友情提示】
一个城市对(i,j),满足i

Data Constraint

30%数据满足n≤10^2,m≤10^3,q≤10^2
60%数据满足n≤10^2,m≤10^3,q≤10^5
100%数据满足n≤10^4,m,q≤10^5,0≤c,Li≤10^9


思路

考场上先是看错题了。。。。。不过并没有什么影响。
想的是预处理答案二分查找,不过竟然爆空间了。。
然后想到离线,不过没时间了。


解法

30%:n^3的Floyd求当前图的所有点之间的路径长度,然而判断即可。时间O(n^3+qn^2).
60%:
对于上面30分做法,我们发现查询部分太大,所以把两点间距离预处理出来后,可以排一次序。对于查询,二分查找即可。时间O(n^3+qlog(n^2))
100%:n到1e3后会爆空间,且Floyd也过不了。
随着非递减的边的加入,同一块里的点互相之间的破损程度是非递减的,加上空间问题,可以考虑离线+并查集。
先把边离线出来按边的破损从小到大排一次序。
把询问也按照破损程度从小到大拍一次序。
对于一个询问,我们把破损程度小于等于这个询问的边逐渐加入,然后把其两端的联通块x,y并起来。
若x,y本来就是联通的,那么答案不会有影响。
若x,y本身不连通,那么联通这两个联通块,同时会造成新的合法点对数量为sum[x]*sum[y](sum[x]表示x联通块的大小)。
时间:O(n+q)(主体)+O(3n log n)(排序)。


代码

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=1e4+5,maxm=1e5+5;
int dad[maxn],sum[maxn];
struct cy{
    int s,t,val;
}a[maxm];
struct hg{
    int val,id,ans;
}que[maxm];
int n,m,q,num,tot,nn;

bool cmp1(cy a,cy b)
{
    return a.val<b.val;
}
bool cmp2(hg a,hg b)
{
    return a.val<b.val;
 }
 bool cmp3(hg a,hg b)
 {
    return a.id<b.id;
 }
int getdad(int x)
{
    if (dad[x]==x) return x;
    else {
        dad[x]=getdad(dad[x]);
        return dad[x];
    }
}
int main()
{
    //freopen("T1.in","r",stdin);
//  freopen("T1.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    fo(i,1,m){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if (x==y) continue;
        a[++tot].s=x,a[tot].t=y,a[tot].val=z;
    }
    sort(a+1,a+tot+1,cmp1);

    fo(i,1,n) dad[i]=i,sum[i]=1;

    fo(i,1,q) scanf("%d",&que[i].val),que[i].id=i;
    sort(que+1,que+1+q,cmp2);

    num=1;nn=0;
    fo(i,1,q){
        int now=que[i].val;
        while (a[num].val<=now&&num<=tot) {
            int x=a[num].s,y=a[num].t;
            int X=getdad(x),Y=getdad(y);
            if (X!=Y) {
                nn+=sum[Y]*sum[X];
                sum[Y]+=sum[X]; sum[X]=0;
                dad[X]=Y;
            }
            ++num;
        }
        que[i].ans=nn;
    }

    sort(que+1,que+1+q,cmp3);
    fo(i,1,q) printf("%d ",que[i].ans);
} 

这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值