【BestCoder Round 65D】【樹形DP 容斥思想】ZYB's Tree 求距離每個節點距離不超過k的節點數
阿新 • • 發佈:2018-12-24
ZYB's Tree
Accepts: 77 Submissions: 513 Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) 問題描述ZYB有一顆N個節點的樹,現在他希望你對於每一個點,求出離每個點距離不超過K的點的個數. 兩個點(x,y)在樹上的距離定義為兩個點樹上最短路徑經過的邊數, 為了節約讀入和輸出的時間,我們採用如下方式進行讀入輸出: 讀入:讀入兩個數A,B,令fai為節點i的父親,fa1=0;輸入描述fai=(A∗i+B)%(i−1)+1 i∈[2,N] . 輸出:輸出時只需輸出N個點的答案的xor和即可。
第一行一個整數T表示資料組數。 接下來每組資料: 一行四個正整數N,K,A,B. 最終資料中只有兩組N≥100000。 1≤T≤5,1≤N≤500000,1≤K≤10,1≤A,B≤1000000輸出描述
T行每行一個整數表示答案.
輸入樣例
1 3 1 1 1輸出樣例
3
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=5e5+10,M=1e6+10,Z=1e9+7,ms63=1061109567; int casenum,casei; int n,K,A,B; int first[N],id; int w[M],nxt[M]; bool e[N]; int f[N][12],ans; inline void ins(int x,int y) { ++id; w[id]=y; nxt[id]=first[x]; first[x]=id; } void dfs(int x) { e[x]=1; f[x][0]=1;for(int i=1;i<=K;++i)f[x][i]=0; for(int z=first[x];z;z=nxt[z]) { int y=w[z]; if(e[y])continue; dfs(y); for(int i=1;i<=K;++i)f[x][i]+=f[y][i-1]; } } void dp(int x) { e[x]=0; for(int z=first[x];z;z=nxt[z]) { int y=w[z]; if(!e[y])continue; for(int i=K;i>=2;--i)f[y][i]+=f[x][i-1]-f[y][i-2];++f[y][1]; dp(y); } int tmp=0; for(int i=0;i<=K;++i)tmp+=f[x][i]; ans^=tmp; } int main() { scanf("%d",&casenum); for(casei=1;casei<=casenum;++casei) { scanf("%d%d%d%d",&n,&K,&A,&B); memset(first,0,n+2<<2);id=0; for(int i=2;i<=n;++i) { int j=((LL)A*i+B)%(i-1)+1; ins(i,j);ins(j,i); } ans=0; dfs(1); dp(1); printf("%d\n",ans); } return 0; } /* 【trick&&吐槽】 1,csy向我透露說,這次BC有題可以暴力過! 於是我就寫了個暴力,然後TLE…… 果然還是要自己思考,不能再被騙了2333 2,A和B都是1e6範圍的數,所以乘法可能會爆int,一定要注意啊>_< 3,這麼水的題比賽時候竟然沒認真想過,我好蠢! 【題意】 給你一棵樹,樹上有n(5e5)個節點,讓你求出,對於所有點而言的,距離不超過K(1<=K<=10)的節點數。 然後輸出這所有節點數的異或和。 【型別】 樹形DP 【分析】 首先,這是樹結構。 然後,我們嘗試簡化問題。 如果求的,不是對於一個節點,所有距離在[1,K]的節點數,而是限制在子樹內的距離在[1,K]的節點數。 那麼,這道題,我們直接一個dfs就可以搞定。 就是從葉子節點開始,距離這個節點距離為[1,K]的節點數。 然後f[x][i]=∑f[son][i-1],i∈[1,K] f[x][0]=1 然而,我們還要求與非子樹內的節點,怎麼辦呢? 我們做完之前的預處理之後,只需要從父節點尋求轉移即可。 我們假設,我們已經知道了距離父節點x距離為0~K的所有點的點數。我們現在要向子節點y轉移。 顯然,f[y][i]+=f[x][i-1]-f[y][i-2],2<=i<=K。 ++f[y][1]; 意思是,距離父節點x為i-1的節點,轉移到節點y的時候,距離就變成了i。 然而, 並非所有的節點都能做轉移。y子樹內的,距離為y為i-2的節點,距離父節點的距離也是i-1,但是距離y的距離並非為i。 所以我們把這些節點剔除。 一個轉移從子節點轉移而來,另外一個轉移從父節點轉移而來。 同時DP的時候要注意使用合理的拓撲序,就可以順利AC這道題啦。啦啦啦啦~ 【時間複雜度&&優化】 O(nk) 【資料】 除錯程式碼—— for(int i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y); ins(x,y); ins(y,x); } input 14 5 1 1 1 2 2 3 3 4 4 5 1 6 6 7 7 8 8 9 9 10 1 11 11 12 12 13 13 14 output 8 */