1. 程式人生 > >hdu3887dfs序+樹狀陣列/線段樹

hdu3887dfs序+樹狀陣列/線段樹

題目:http://acm.hdu.edu.cn/showproblem.php?pid=3887
大意:求在當前點的所有後代中,後代點的序號大小<當前點的序號大小,並統計他們的個數,這就是f[i]。
思路:dfs序+樹狀陣列/線段樹
dfs序:https://www.cnblogs.com/stxy-ferryman/p/7741970.html
dfs序:dfs是深度優先的,所以對於一個點,它會先遍歷完它的所有子節點,再去遍歷他的兄弟節點以及其他所以對於一棵樹的dfs序來說,這個點和他所有的子節點會被儲存在連續的區間之中。

怎樣統計後代序號比它小的個數之和?
首先經dfs遍歷之後每個點以及他的子節點都被儲存在一個連續的區間內,原來的序號變成了該連續區間的下標,找比它序號小的就直接往前面找並且前面點的區間必須包含在該區間內(dfs後一個點的子樹所在的區間一定是在該點的區間內)
已根節點7為例:dfs後in[7]=1,out[7]=15,節點為7的區間包含所有的點,所以直接在前面找,7錢面有6個點,所以f[7]=6;
節點10:in[10]=2,out[10]=5,子區間有三個,in[14]=3,out[14]=5; in[2]=4,out[2]=4;
in[13]=5,out[13]=5;但是10前面只有節點2的區間在節點10內。所以f[10]=1;
上程式碼:
樹狀陣列:

#pragma comment(linker,"/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#define max(a,b) a>b? a:b
#define len 100005
using namespace std;
int head[len],n,root,k,cnt,in[len],out[len];
struct node
{
	int j,next;
}s[len*2];
struct text
{
	int l,r,sum;
}ans[len];
int c[len];
void add(int a,int b)
{
	s[k].j=b;
	s[k].next=head[a];
	head[a]=k++;
}
void dfs(int x,int pre)
{
	in[x]=++cnt;
	for(int i=head[x];i!=-1;i=s[i].next)
	{
		int j=s[i].j;
		if(j!=pre) dfs(j,x);
	}
	out[x]=cnt;
}
int lowbit(int x)
{
	return x&(-x);
}
void update(int x,int p)
{
   for(int i=x;i<=n;i+=lowbit(i)) c[i]+=p;
   return ;
}
int sum(int x)
{
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i)) sum+=c[i];
	return sum;
}
int main()
{
	while(scanf("%d%d",&n,&root)!=EOF&&n&&root)
	{
		cnt=0;
		k=0;
		int a,b;
		memset(head,-1,sizeof(head));
		memset(c,0,sizeof(c));
		for(int i=0;i<n-1;i++)
		{
			scanf("%d%d",&a,&b);
			add(a,b);
			add(b,a);
		}
		dfs(root,-1);
	    for(int i=1;i<=n;i++) 
		{
			 printf("%d%s",sum(out[i])-sum(in[i]-1),i==n? "\n":" ");
			 update(in[i],1);
		}
	}
	return 0;
}
#pragma comment(linker,"/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<vector>
#define max(a,b) a>b? a:b
#define len 200005
using namespace std;
int n,root,k,cnt,in[len],out[len];
bool vis[len];
vector<vector<int> > t(len);
struct text
{
	int l,r,sum;
}ans[len<<2];
void dfs(int x)
{
	vis[x]=1;
	in[x]=++cnt;
	for(int i=0;i<t[x].size();i++)
	{
		if(!vis[t[x][i]]) dfs(t[x][i]);
	}
	out[x]=cnt;
}
void pushup(int i)
{
	ans[i].sum=ans[i*2].sum+ans[i*2+1].sum;
}
void build(int l,int r,int i)
{
	ans[i].l=l;
	ans[i].r=r;
	ans[i].sum=0;
	if(ans[i].l==ans[i].r) return ;
	int mid=(ans[i].l+ans[i].r)/2;
	build(l,mid,i*2);
	build(mid+1,r,i*2+1);
}
void update(int l,int r,int i)
{
	if(ans[i].l==l&&ans[i].r==r)
	{
		ans[i].sum=1;
		return ;
	}
    int mid=(ans[i].l+ans[i].r)/2;
    if(mid>=r) 	update(l,r,i*2);
    else if(mid<l)	update(l,r,i*2+1);
    else
	{	
		update(l,mid,i*2);
		update(mid+1,r,i*2+1);	
	}
	pushup(i);
}
int query(int l,int r,int i)
{
	if(ans[i].l==l&&ans[i].r==r) return ans[i].sum;
    int mid=(ans[i].l+ans[i].r)/2;
	if(mid>=r) query(l,r,i*2);
	else if(mid<l)  query(l,r,i*2+1);
	else return query(l,mid,i*2)+query(mid+1,r,i*2+1);
}
int main()
{
	while(scanf("%d%d",&n,&root)!=EOF&&n&&root)
	{
		cnt=0;
		k=0;
		int a,b;
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=n;i++) t[i].clear();
		for(int i=0;i<n-1;i++)
		{
			scanf("%d%d",&a,&b);
			t[a].push_back(b);
			t[b].push_back(a);
		}
		dfs(root);
		build(1,n,1);
	    for(int i=1;i<=n;i++) 
		{
			 printf("%d%s",query(in[i],out[i],1),i==n? "\n":" ");
			 update(in[i],in[i],1);
		}
	}
	return 0;
}

相關推薦

hdu3887dfs+陣列/線段

題目:http://acm.hdu.edu.cn/showproblem.php?pid=3887 大意:求在當前點的所有後代中,後代點的序號大小<當前點的序號大小,並統計他們的個數,這就是f[i]。 思路:dfs序+樹狀陣列/線段樹 dfs序:https

noip提高組資料結構模板[並查集,st表,陣列,線段]

/*資料結構*/ //並查集 for(int i=1;i<=n;i++) fa[i]=i;*** int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} //st表 for(int i=2;i<=n;i++) Log[i]=Lo

模板三連擊:陣列+線段+主席

沒事兒幹,複習模板...... 1.樹狀陣列 本來不想寫這個的,但是反正就幾分鐘就打完了,所以就寫了,水AC數。 洛谷 P3374 [模板]樹狀陣列 1 1 #include<cstdio> 2 3 typedef long long ll;

【2018ccpc區域賽網路賽】【hdu6447 YJJ's Salesman】【dp+離散化+陣列/線段優化】

連結: 分析:二維座標排序,x->大,y->小,由於我們每次走必須x,y均變大,那麼相當於只要考慮排序後的y的值。從左往右考慮y,dp[i]=max(dp[j])+val[i](i表示第i個點),由於y的資料範圍為1e9,需要離散化,然後用樹狀陣列維護求最大

HDU1166+POJ3468 陣列+線段

題目大意: 第一行一個整數T,表示有T組資料。 每組資料第一行一個正整數N(N<=50000),表示敵人有N個工兵營地,接下來有N個正整數,第i個正整數ai代表第i個工兵營地裡開始時有ai個人(1<=ai<=50)。 接下來每行有

Apple Tree(陣列+線段

Apple Tree Description: There is an apple tree outside of kaka’s house. Every autumn, a lot of apples will grow in the tree. Kaka

HDU 1166-敵兵佈陣【陣列&&線段單點更新】【模板】

C國的死對頭A國這段時間正在進行軍事演習,所以C國間諜頭子Derek和他手下Tidy又開始忙乎了。A國在海岸線沿直線佈置了N個工兵營地,Derek和Tidy的任務就是要監視這些工兵營地的活動情況。由於採取了某種先進的監測手段,所以每個工兵營地的人數C國都掌握的一清二楚,每個工兵營地的人數都有可能發生變動,可

bzoj[3881]Divljak(dfs+陣列+fail)

這道題利用了fail樹的神奇性質————父節點為其子節點的字首 先對Alice的集合建一個fail樹, Bob每插入一個串,都將串在自動機上經過的點在樹上打上標記(+1) 每次查詢的答案就是詢問串的結束節點的子樹的貢獻 所以還需要用到樹狀陣列來維護dfs序 因為Bob的一個串至多隻能對Alice的某

【動態主席】ZOJ 2112【陣列+主席

題意:       給定一個區間,求這個區間第k小的數,支援單點修改。   思路:       動態主席樹裸題。       我們先來回顧一下靜態主席樹的做法,對於陣列中每一個位置

【BZOJ1146】網路管理(CTSC2008)-陣列+主席

測試地址:網路管理 做法: 本題需要用到樹狀陣列+主席樹。 經典的帶修改樹上路徑第kkk大問題,不過我太菜了居然忘了有樹上主席樹這個東西… 不帶修改的話能用樹上主席樹做,那麼帶修改怎麼辦呢?因為一次修改會影響一棵子樹上的所有線段樹,所以我們還是把樹拍成DFS序,

對的三種求法(歸併排序,陣列線段)

求逆序對個數的三種方法 逆序對: 對於一個序列 $a_1$,$a_2$,$a_3$..$a_n$,如果存在$a_i$>$a_j$且i<j,則$a_i$和$a_j$為一個逆序對。 這裡將介紹3種求逆序對對數的方法。 在此之前,預設為你已經會了歸併排序,樹狀陣列和線段樹。(不會的可以百度學習一下)

2018.11.07【CQOI2011】【BZOJ3295】【洛谷P3157】動態逆對(陣列套動態開點線段

BZOJ傳送門 洛谷傳送門 解析: 首先我們可以通過一個線段樹求出逆序對個數,然後就是亂搞的時間了。 顯然每次刪除一個數,需要我們查詢前面比他大的數的個數和後面比他小的數的個數,這個就是裸的樹套樹了。這道題可以用樹狀陣列套線段樹動態開點。 程式碼: #

求逆對個數的三種方法(歸併排序,陣列,權值線段)

求逆序對個數的三種方法 逆序對: 對於一個序列 a1a_1a1​,a2a_2a2​,a3a_3a3​…ana_nan​,如果存在aia_iai​>aja_jaj​且i<j,則aia_iai​和aja_jaj​為一個逆序對。 這裡將介紹3種求逆序對對數

BZOJ 3295 [Cqoi2011]動態逆陣列線段

題意:連結 方法:樹狀陣列套線段樹 解析: 這題基本上寫的都是什麼CDQ點分治,主席樹之類的,然而這我都並不會,所以寫了一發平衡樹套線段樹想卡時卡過去,然而我並沒有得逞,T的不要不要的,這裡用平衡樹套線段樹的方法參見我的題解:排隊。這道題比那道

poj 3321 dfs 陣列 前向星

  題意概括 有一顆01樹,以結點1為樹根,一開始所有的結點權值都是1,有兩種操作:   1.改變其中一個結點的權值(0變1,1變0)   2.詢問子樹X的節點權值和。 參考部落格 http://www.cnblogs.com/zhouzhendong/p/7265431.htm

【非原創】codeforces 1070C Cloud Computing 【線段&陣列

題目:戳這裡 學習部落格:戳這裡 題意:有很多個活動,每個活動有持續天數,每個活動會在每天提供C個CPU每個CPU價格為P,問需要工作N天,每天需要K個CPU的最少花費。 解題思路:遍歷每一天,維護當前天K個cpu的最小花費。具體方法是維護兩個線段樹(樹狀陣列也可以),維護每一天可以使用的cpu數和價格

[zoj4046][陣列求逆(強化版)]

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4046 題意:有一個含有n個元素的數列p,每個元素均不同且為1~n中的一個,求出將數列變為迴圈遞增序列至少需要左右相鄰的數交換多少次 題目分析:先看簡化版的題目:如果只有1 2 3

【11.2校內測試】【壓】【矩陣字首和】【陣列對(題意轉換)】

Solution 簽到水題,直接狀壓列舉所有情況算出答案即可。 Code #include<bits/stdc++.h> #define LL long long using namespace std; inline LL read() { LL x =

離散化及陣列求逆

用樹狀陣列求逆序對的時候注意:要統計b[i]-1的字首和,因為可能有相同值的元素 不去重離散化: sort(a+1, a+n+1, cmp); for(int i=1; i<=n; i++) { if(i == 1 || a[i].val != a[i-1].val) { to

Luogu P3258 松鼠的新家(鏈剖分+線段/陣列

題面 題解   這種題目一看就是重鏈剖分裸題,還是區間修改,單點查詢,查詢之前在遍歷時要記一個\(delta\),因為這一次的起點就是上一次的終點,不需要放糖,所以可以用\(BIT\)來寫,但我寫完\(modify\)才反應過來,所以沒改了。 #include <cstdio> #inclu