ICPC SWERC 2018 Travel Guide (最短路+CDQ分治)

题目描述:

Paris counts many hotels. Some are very close to the Orly airport, which is very useful to spend a night before an early flight. Some are very close to the Notre-Dame cathedral which allows tourists to walk around the “Île Saint-Louis” at dawn and enjoy the banks of the Seine. Finally, some are closer to the Disneyland Paris resort which is the most visited tourist attraction. Travelers who come to Paris usually want to stay near these three main Points Of Interest (POIs): Orly, Notre-Dame, and Disneyland.

You are employed by a travel agency and your manager Anna wants to prepare a list of hotels to include in her new travel guide. Her guide contains one entry per station of the metropolitan network.

Anna notices that some stations do not have a convenient location with respect to the distance to the three POIs and therefore her guide should not contain a hotel section for such “useless” stations.

Anna considers that a station is useless when another station is closer to all the POIs. Formally a station A is useless when there exists another station B such that B is at least as close to the three POIs as A is and B is strictly closer than A to at least one of those POIs. A station is useful if it is not useless.

Anna asks you how many stations in her guide will have a hotel section. To compute this list you are given the plan of the metropolitan network. The plan of the metropolitan network is an undirected weighted graph. In this graph, each node corresponds to a station (note that each POI is also a station); each edge links two stations and takes a certain time to be traversed in either direction. This graph is connected and the distance between any two stations is the lowest total time of a path between the
two nodes.

题目大意:

给你一张图,有n个点,m条边,图是全联通的,每个点到0,1,2都有最短路dis0,dis1,dis2,对于一个点A,如果存在另一个点B,B的dis1,dis2,dis3不大于A的dis1,dis2,dis3,且其中有一个dis严格小于A中相应的dis,我们就说A是没用的。
题目求有多少个点不是没用的点(包括点0,1,2)
(n<=1e5,m<=5e5)

题解:

先对0,1,2分别求单源最短路得到每个点的三个属性。
其实就是对于每一个(x,y,z),求是否存在(x’,y’,z’)使得(x’<=x,y’<=y,z’<=z)且这三个值并不完全相等。把三个值相等的点合并之后就是求三维偏序问题了。CDQ分治练习题。

#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) (x&(-x))
#define P pair<int, int>
#define pb push_back
using namespace std;
const int maxn = 1e5 + 50;
const int inf = 0x3f3f3f3f;
int dis1[maxn], dis2[maxn], dis3[maxn];
int a[maxn];
void add(int i, int x){while(i < maxn)a[i] += x, i += lowbit(i);}
int qry(int i){int res = 0;while(i) res += a[i], i -= lowbit(i);return res;}
int n, m;
vector<P> g[maxn];
priority_queue<P, vector<P>, greater<P> > q;
void dij(int *dis, int st){
    for(int i = 0; i <= n; ++i) dis[i] = inf;
    dis[st] = 0;
    while(q.size()) q.pop();
    q.push(P(dis[st], st));
    while(q.size()){
        P temp = q.top(); q.pop();
        int u = temp.second;
        if(temp.first > dis[u]) continue;
        for(int i = 0; i < g[u].size(); ++i){
            int w = g[u][i].first;
            int v = g[u][i].second;
            if(dis[u] + w < dis[v]){
                dis[v] = dis[u] + w;
                q.push(P(dis[v], v));
            }
        }
    }
}
struct node{
    int x, y, z;
    int id, cnt;
    bool operator < (const node& a)const{
        if(x!=a.x) return x < a.x;
        if(y!=a.y) return y < a.y;
        if(z!=a.z) return z < a.z;
    }
}e[maxn],temp[maxn];
int vis[maxn];
int cc[maxn];
int num = 0;
int sum;
void init()
{
    scanf("%d%d", &n, &m);
    sum = n;//总共的点数
    while(m--){
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        g[u].pb(P(w, v)); g[v].pb(P(w, u));
    }
    dij(dis1, 0);
    dij(dis2, 1);
    dij(dis3, 2);
    num = 0;
    for(int i = 0; i < n; ++i)  {
        e[i].x = dis1[i], e[i].y = dis2[i], e[i].z = dis3[i], cc[++num] = e[i].z;
    }
    sort(cc+1,cc+1+num);
    num = unique(cc+1,cc+1+num)-cc-1;
    for(int i = 0; i < n; ++i) e[i].z = lower_bound(cc+1,cc+1+num,e[i].z)-cc;
    sort(e, e + n);
    int p = 1;
    int tot = 0;
    for(int i = 0; i < n; ++i){//处理三个值都相等的
        if(e[i].x == e[i+1].x && e[i].y == e[i+1].y && e[i].z == e[i+1].z){
            p++; continue;
        }
        e[i].cnt = p;//记录相同的点的个数
        e[tot++] = e[i];
        p = 1;
    }
    n = tot;
    for(int i = 0; i < n; ++i) e[i].id = i;//最后再定每个点对应坐标
}
void cdq(int l, int r)
{
    if(r-l <= 1) return;
    int mid = (r+l)>>1;
    cdq(l, mid); cdq(mid, r);
    int lp = l, rp = mid, o = 0;
    while(lp < mid && rp < r){
        if(e[lp].y <= e[rp].y){
            add(e[lp].z, 1);
            temp[o++] = e[lp++];
        }
        else{
            int res = qry(e[rp].z);
            if(res != 0) vis[e[rp].id] = e[rp].cnt;
            temp[o++] =e[rp++];
        }
    }
    while(rp < r){
        int res = qry(e[rp].z);
        if(res != 0) vis[e[rp].id] = e[rp].cnt;
        temp[o++] = e[rp++];
    }
    for(int i = l; i < lp; ++i){//
        add(e[i].z, -1);//恢复树状数组(i写成lp又debug一小时:)
    }
    while(lp < mid) temp[o++] = e[lp++];
    for(int i = 0; i < o; ++i) e[i+l] = temp[i];
    return;
}
void sol()
{
    memset(vis, 0, sizeof vis);
    cdq(0, n);
    for(int i = 0; i < n; ++i) {
        sum -= vis[i];
    }
    cout<<sum<<endl;
}
int main()
{
	init();sol();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值