[codeforces161D]Distance in Tree(点分治/树形dp)
题意:求树上距离为k的点对个数;
解题关键:练习一下点分治不用容斥 而直接做的做法。注意先查询,后更新。
不过这个方法有个缺陷,每次以一个新节点为根,必须memset mp数组,或许使用map会好些,更新序号一类用ca这种形式更好些。
试了一下,map更慢,应该是带log的原因。
点分治解法:
1 #pragma comment(linker,"/STACK:102400000,102400000") 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cstdlib> 6 #include<iostream> 7 #include<cmath> 8 #include<map> 9 #define maxn 100040 10 #define maxm 1000500 11 using namespace std; 12 typedef long long ll; 13 const ll mod=1000003; 14 const ll inf=1ll<<60; 15 ll n,k,ans,size,s[maxn],f[maxn],path[maxn],cr; 16 ll head[maxn],cnt,root; 17 bool vis[maxn]; 18 struct edge{ 19 ll to,nxt; 20 }e[maxn<<1]; 21 map<int,int>mp; 22 void add_edge(ll u,ll v){ 23 e[cnt].to=v; 24 e[cnt].nxt=head[u]; 25 head[u]=cnt++; 26 } 27 28 inline ll read(){ 29 char k=0;char ls;ls=getchar();for(;ls<'0'||ls>'9';k=ls,ls=getchar()); 30 ll x=0;for(;ls>='0'&&ls<='9';ls=getchar())x=(x<<3)+(x<<1)+ls-'0'; 31 if(k=='-')x=0-x;return x; 32 } 33 34 void get_root(ll u,ll fa){//get_root会用到size 35 s[u]=1;f[u]=0;//f是dp数组 36 for(ll i=head[u];i!=-1;i=e[i].nxt){ 37 ll v=e[i].to; 38 if(v==fa||vis[v]) continue; 39 get_root(v,u); 40 s[u]+=s[v]; 41 f[u]=max(f[u],s[v]); 42 } 43 f[u]=max(f[u],size-s[u]); 44 root=f[root]>f[u]?u:root; 45 } 46 47 void get_path_size(ll u,ll fa,ll dis){ 48 if(dis+1<=k){ 49 path[cr]=dis+1; 50 cr++; 51 } 52 s[u]=1; 53 for(ll i=head[u];i!=-1;i=e[i].nxt){ 54 ll v=e[i].to; 55 if(v==fa||vis[v]) continue; 56 get_path_size(v,u,dis+1); 57 s[u]+=s[v]; 58 } 59 } 60 61 void work(ll u,ll fa){ 62 vis[u]=true; 63 mp.clear(); 64 mp[0]=1; 65 for(ll i=head[u];i!=-1;i=e[i].nxt){ 66 ll v=e[i].to; 67 if(v==fa||vis[v]) continue; 68 cr=0; 69 get_path_size(v,u,0); 70 for(ll j=0;j<cr;j++){ 71 ans+=mp[k-path[j]]; 72 } 73 for(int j=0;j<cr;j++){ 74 mp[path[j]]++; 75 } 76 } 77 for(ll i=head[u];i!=-1;i=e[i].nxt){ 78 ll v=e[i].to; 79 if(vis[v]||v==fa) continue; 80 size=s[v],root=0; 81 get_root(v,u); 82 work(root,u); 83 } 84 } 85 86 void init(){ 87 memset(vis,0,sizeof vis); 88 memset(head,-1,sizeof head); 89 ans=cnt=0; 90 } 91 92 int main(){ 93 ll a,b; 94 f[0]=inf; 95 while(scanf("%I64d%I64d",&n,&k)!=EOF){ 96 init(); 97 for(int i=0;i<n-1;i++){ 98 a=read(),b=read(); 99 add_edge(a,b); 100 add_edge(b,a); 101 } 102 size=n,root=0; 103 get_root(1,-1); 104 work(root,-1); 105 printf("%d\n",ans); 106 } 107 return 0; 108 }
树形dp解法:
复杂度:$O(nk)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 #define maxn 100006 5 int head[maxn],cnt,dp[maxn][502]; 6 struct edge{ 7 int to,w,nxt; 8 }e[maxn<<1]; 9 ll ans; 10 int n,k,a,b; 11 void add_edge(int u,int v){ 12 e[cnt].to=v; 13 e[cnt].nxt=head[u]; 14 head[u]=cnt++; 15 } 16 17 void dfs(int u,int fa){ 18 dp[u][0]=1; 19 for(int i=head[u];i!=-1;i=e[i].nxt){ 20 int v=e[i].to; 21 if(v==fa) continue; 22 dfs(v,u); 23 for(int j=1;j<=k;j++){ 24 dp[u][j]+=dp[v][j-1]; 25 } 26 } 27 ans+=dp[u][k]; 28 int tmp=0; 29 for(int i=head[u];i!=-1;i=e[i].nxt){ 30 int v=e[i].to; 31 if(v==fa) continue; 32 for(int j=1;j<k;j++){ 33 tmp+=1ll*dp[v][j-1]*(dp[u][k-j]-dp[v][k-j-1]); 34 } 35 } 36 ans+=tmp/2; 37 } 38 39 void init(){ 40 memset(head,-1,sizeof head); 41 cnt=0; 42 ans=0; 43 } 44 45 int main(){ 46 init(); 47 cin>>n>>k; 48 for(int i=0;i<n-1;i++){ 49 cin>>a>>b; 50 add_edge(a,b); 51 add_edge(b,a); 52 } 53 dfs(1,-1); 54 cout<<ans<<"\n"; 55 return 0; 56 }