1. 程式人生 > >藍橋杯模擬賽 青出於藍而勝於藍

藍橋杯模擬賽 青出於藍而勝於藍

葉子 我們 樹狀數組 for def rip ear 空格 輸入

武當派一共有 nn 人,門派內 nn 人按照武功高低進行排名,武功最高的人排名第 11,次高的人排名第 22,... 武功最低的人排名第 nn。現在我們用武功的排名來給每個人標號,除了祖師爺,每個人都有一個師父,每個人可能有多個徒弟。

我們知道,武當派人才輩出,連祖師爺的武功都只能排行到 pp。也就是說徒弟的武功是可能超過師父的,所謂的青出於藍勝於藍。

請你幫忙計算每個人的所有子弟(包括徒弟的徒弟,徒弟的徒弟的徒弟....)中,有多少人的武功超過了他自己。

輸入格式

輸入第一行兩個整數 n, p(1 \le n \le 100000, 1 \le p \le n)n,p(1n100000,1pn)。

接下來 n-1n?1 行,每行輸入兩個整數 u, v(1 \le u, v \le n)u,v(1u,vn),表示 uu 和 vv 之間存在師徒關系。

輸出格式

輸出一行 nn 個整數,第 ii 個整數表示武功排行為 ii 的人的子弟有多少人超過了他。

行末不要輸出多余的空格。

樣例輸入

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

樣例輸出

0 0 2 0 4 0 1 2 0 0

dfs序列+樹狀數組
利用dfs序列把樹序列化,並可以用時間戳來維護一個非葉子節點的子樹。對於一個區間 求這個區間內比某個值要小的值的個數,利用權值線段樹的思想
代碼:
#include <cstdio>
#include <cstring>
#include <iostream>
#include 
<vector> #include <queue> using namespace std; const int maxn=100010; int n,m; vector<int> edge[maxn]; int in[maxn*2],out[maxn*2]; int ret; int tree[maxn*2]; int lowbit(int t) { return t&(-t); } void up(int x,int y) { for(int i=x;i<=n;i+=lowbit(i)) tree[i]+=y; }
int getsum(int x) { int ans=0; for(int i=x;i>0;i-=lowbit(i)) ans+=tree[i]; return ans; } void init() { memset(tree,0,sizeof(tree)); ret=0; for(int i=0;i<=n;i++) edge[i].clear(); } void dfs(int u,int fa) { in[u]=++ret; int len=edge[u].size(); for(int i=0;i<len;i++) { if(edge[u][i]!=fa) { dfs(edge[u][i],u); } } out[u]=ret; } int main() { scanf("%d %d",&n,&m); init(); for(int i=1;i<n;i++) { int x,y; scanf("%d %d",&x,&y); edge[x].push_back(y); edge[y].push_back(x); } dfs(m,m); for(int i=1;i<=n;i++) { cout<<getsum(out[i])-getsum(in[i]); if(i!=n) cout<<" "; up(in[i],1); } cout<<endl; return 0; }

藍橋杯模擬賽 青出於藍而勝於藍