4227: 城市

44 篇文章 0 订阅
13 篇文章 0 订阅

4227: 城市

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 29   Solved: 10
[ Submit][ Status][ Discuss]

Description

A国是一个拥有n个城市的国家,其中城市s是A国的首都。
A国还有m条道路,每条道路连着两个不同的城市,但是一对城市间可能有多条道路。每一条道路都有它的长度,一条道路的通行时间与一条道路的长度成正比。
你作为A国的统治者,设计出了一种统计城市重要程度的方法:
1、一条道路的重要度为:在这条道路不能使用的情况下,到首都s的最短时间会变长的城市的数目。
2、一个城市的重要度为:以它作为一端的所有道路的重要度的和。
现在,你知道了A国的道路连接情况,你需要计算出每一个城市的重要度。

Input

第一行,两个整数n,m,表示有A国有n个城市及m条道路。
第2~m+1行,每行三个整数u,v,l,描述了一条道路的两个端点城市及长度。
第m+2行,一个整数s,表示A国首都的编号

Output

n行,每行一个整数,第i行为编号i的城市的重要度。 

Sample Input

4 4
1 2 3
2 3 4
3 4 5
4 1 2
1

Sample Output

2
1
0
1

HINT

100%的数据,1<=n<=1000001<=m<=2000001<=l<=10^8

Source

[ Submit][ Status][ Discuss]



对于原图,不妨先构建出他的最短路径树

那么显然,所有不在最短路径树上的边,重要度一定为0

因为无论这些边哪条被删除,反正每个城市总是可以沿着最短路径树走到首都的

现在考虑每个点对哪些边可能有贡献

假设任取一个点x,那么x只可能对最短路径树上路径(s,x)上的边产生贡献

证明也是显然的,别的边不管怎样删除,(s,x)总是不变

倒过来看每条边,可能对它有贡献的节点,就只可能存在于它下方节点的子树了

假设能预处理数组MinL[x]:从点x出发,不经过任何路径(s,x)上的边,所能达到的点中深度最小的是多少

显然,这样能达到的点必须满足这样走的路径不会更劣,即修改后仍等价于x的最短路

现在对这棵最短路径树作一个dfs,递归地处理每条边的重要度

假设当前处理节点x,则考虑x为根的子树中有多少点对边(x,fa[x])产生贡献

对于x的所有后代,此时删除的边是(x,fa[x]),说明这些后代本身可以先走到x,然后走MinL[x]这条路径

进一步讲,这个时候对于x的任意后代y,它能达到的最浅深度是min{MinL[x]~MinL[y]}

因为路径(x,y)上的边并没有被删除,y可以先往上走一段在往上跳

我们只需要知道y对边(x,fa[x])有没有贡献,所以只需要知道y能不能跳到x之上就行了

以及,在x之上的点的MinL值不能帮助y往上,因为边(x,fa[x])此时是删除状态

所以可以维护某种数据结构,支持每次将里面的元素和某个数取min,查询大于等于某个数的数有多少

以及,显然需要不断地合并后代信息,这样的数据结构还要可并

这样到达x的时候用MinL[x]更新所有子孙的结果,递归往上就行了

这样的结构可以采用线段树,因为线段树合并的均摊复杂度是O(nlogn)


还剩一个问题,MinL数组如何处理

先对所有节点按照dis从小到大排好序,按这个顺序处理

因为要走非树边,而有用的非树边还要满足走了之后最短路径不增加

所以要处理一个点的时候,只需保证所有dis小于它的点处理完了就行,可能的前驱肯定在里面

对于所有可能的这样的前驱分类讨论,假设一个合法的前驱为点y

如果y在路径(s,x)上,那么直接用MinL[y]更新

否则,先求出x,y的lca,则min{MinL[lca] ~ MinL[y]}都可以更新MinL[x]


一开始选择了启发式合并的treap作需要的数据结构,but....

O(nlog^2n)的理论复杂度跑随机生成的极限数据居然要200s上下?!!

而且。。线段树合并显然更好写呀。。。就这样耗了一个下午。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#include<cstdlib>
using namespace std;
 
const int maxn = 1E5 + 10;
const int maxm = maxn * 2;
const int INF = ~0U>>1;
const int T = 18;
const int M = 15;
typedef long long LL;
 
struct E{
    int to,w,Num; E(){}
    E(int to,int w,int Num): to(to),w(w),Num(Num){}
};
 
struct data{
    int Num; LL dis; data(){}
    data(int Num,LL dis): Num(Num),dis(dis){}
    bool operator < (const data &B) const {return dis < B.dis;}
}D[maxn];
 
int n,m,s,tot,MinL[maxn],fa[maxn][T],Best[maxn][T],id[maxn],L[maxn],cnt[maxm]
    ,Ans[maxn],rt[maxn],lc[maxn*M],rc[maxn*M],siz[maxn*M],pool[maxn*M];
LL dis[maxn]; bool vis[maxn],Mark[maxn*M];
 
vector <E> v[maxn];
vector <int> g[maxn];
queue <int> Q;
 
int LCA(int p,int q)
{
    if (L[p] < L[q]) swap(p,q);
    for (int i = T - 1; i >= 0; i--)
        if (L[p] - (1 << i) >= L[q]) p = fa[p][i];
    if (p == q) return p;
    for (int i = T - 1; i >= 0; i--)
        if (fa[p][i] != fa[q][i])
            p = fa[p][i],q = fa[q][i];
    return fa[p][0];
}
 
void Dfs1(int x)
{
    for (int i = 1; i < T; i++) fa[x][i] = fa[fa[x][i-1]][i-1];
    for (int i = 0; i < g[x].size(); i++)
    {
        int to = g[x][i];
        L[to] = L[x] + 1; Dfs1(to);
    }
}
 
int Search(int x,int y)
{
    int ret = INF;
    for (int now = 0; y; y >>= 1,++now)
        if (y & 1) ret = min(ret,Best[x][now]),x = fa[x][now];
    return ret;
}
 
void Pre_Work()
{
    for (int i = 1; i <= n; i++) D[i] = data(i,dis[i]);
    sort(D + 1,D + n + 1); L[s] = 1; Dfs1(s);
    for (int i = 1; i <= n; i++)
    {
        int k = D[i].Num; MinL[k] = L[k];
        for (int j = 0; j < v[k].size(); j++)
        {
            E e = v[k][j]; if (e.Num == id[k]) continue;
            if (1LL * e.w + dis[e.to] > dis[k]) continue;
            int lca = LCA(e.to,k);
            MinL[k] = min(MinL[k],MinL[e.to]);
            MinL[k] = min(MinL[k],Search(e.to,L[e.to] - L[lca]));
        }
        Best[k][0] = MinL[fa[k][0]];
        for (int i = 1; i < T; i++)
            Best[k][i] = min(Best[k][i-1],Best[fa[k][i-1]][i-1]);
    }
}
 
void pushdown(int o)
{
    if (!Mark[o]) return;
    if (lc[o]) Mark[lc[o]] = 1;
    if (rc[o]) Mark[rc[o]] = 1;
    Mark[o] = siz[o] = 0;
}
 
int New_Node()
{
    int ret = pool[tot--];
    lc[ret] = rc[ret] = 0;
    siz[ret] = Mark[ret] = 0;
    return ret;
}
 
void Merge(int &o1,int o2,int l,int r)
{
    pushdown(o1); pushdown(o2);
    if (!o1) {o1 = o2; return;}
    if (!o2) return; siz[o1] += siz[o2];
    if (l == r) {pool[++tot] = o2; return;}
    int mid = l + r >> 1;
    Merge(lc[o1],lc[o2],l,mid);
    Merge(rc[o1],rc[o2],mid+1,r);
    pool[++tot] = o2;
}
 
int Set_Mark(int o,int l,int r,int ql,int qr)
{
    if (!o) return 0; pushdown(o);
    if (ql <= l && r <= qr)
    {
        Mark[o] = 1; int ret = siz[o];
        pushdown(o); return ret;
    }
    int mid = l + r >> 1,ret = 0;
    if (ql <= mid) ret = Set_Mark(lc[o],l,mid,ql,qr); else pushdown(lc[o]);
    if (qr > mid) ret += Set_Mark(rc[o],mid+1,r,ql,qr); else pushdown(rc[o]);
    siz[o] = siz[lc[o]] + siz[rc[o]]; return ret;
}
 
void Insert(int &o,int l,int r,int pos,int k)
{
    if (!o) o = New_Node(); pushdown(o);
    siz[o] += k; if (l == r) return;
    int mid = l + r >> 1;
    if (pos <= mid) Insert(lc[o],l,mid,pos,k);
    else Insert(rc[o],mid+1,r,pos,k);
}
 
int Query(int o,int l,int r,int ql,int qr)
{
    if (!o) return 0; pushdown(o);
    if (ql <= l && r <= qr) return siz[o];
    int mid = l + r >> 1,ret = 0;
    if (ql <= mid) ret = Query(lc[o],l,mid,ql,qr);
    if (qr > mid) ret += Query(rc[o],mid+1,r,ql,qr);
    return ret;
}
 
void Dfs2(int x)
{
    for (int i = 0; i < g[x].size(); i++)
    {
        int to = g[x][i]; Dfs2(to);
        Merge(rt[x],rt[to],1,n);
    }
    if (x == s) return;
    int sum = Set_Mark(rt[x],1,n,MinL[x],n);
    Insert(rt[x],1,n,MinL[x],sum + 1);
    cnt[id[x]] = Query(rt[x],1,n,L[x],n);
}
 
void SPFA()
{
    for (int i = 1; i <= n; i++) dis[i] = 1E16;
    dis[s] = 0; vis[s] = 1; Q.push(s);
    while (!Q.empty())
    {
        int k = Q.front(); Q.pop(); vis[k] = 0;
        for (int i = 0; i < v[k].size(); i++)
        {
            E e = v[k][i];
            if (dis[e.to] > dis[k] + 1LL * e.w)
            {
                dis[e.to] = dis[k] + 1LL * e.w; fa[e.to][0] = k;
                id[e.to] = e.Num; if (!vis[e.to]) vis[e.to] = 1,Q.push(e.to);
            }
        }
    }
    for (int i = 1; i <= n; i++)
        if (fa[i][0]) g[fa[i][0]].push_back(i);
}
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
        freopen("1.out","w",stdout);
    #endif
     
    n = getint(); m = getint();
    for (int i = 1; i < maxn * M; i++) pool[i] = ++tot;
    for (int i = 1; i <= m; i++)
    {
        int x = getint(),y,w;
        y = getint(); w = getint();
        v[x].push_back(E(y,w,i));
        v[y].push_back(E(x,w,i));
    }
    s = getint(); SPFA(); Pre_Work(); Dfs2(s);
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < v[i].size(); j++)
            Ans[i] += cnt[v[i][j].Num];
    //for (int i = 1; i <= m; i++) printf("%d\n",cnt[i]);
    for (int i = 1; i <= n; i++) printf("%d\n",Ans[i]);
    //cerr << (double)(clock()) / CLOCKS_PER_SEC << endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值