4227: 城市
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 29 Solved: 10
[ Submit][ Status][ Discuss]
Description
Input
Output
n行,每行一个整数,第i行为编号i的城市的重要度。
Sample Input
1 2 3
2 3 4
3 4 5
4 1 2
1
Sample Output
1
0
1
HINT
100%的数据,1<=n<=100000,1<=m<=200000,1<=l<=10^8
Source
对于原图,不妨先构建出他的最短路径树
那么显然,所有不在最短路径树上的边,重要度一定为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;
}