我记得大一的时候 ,听说过楼jz的男人八题,貌似这好像是其中一题。
最近做的一个题: http://codeforces.com/contest/161/problem/D 范围: n=5e4 ,k=500 因为k 很小,所以我们可以用树形dp 解决这个问题。 dp[i][j] : i的子树 dis=j的有多少个点。 u-v dp[u][i]+=dp[v][i-1] 但是复杂度大概有n*n 不可取啊 哦,因为 k 只有500 所以我们只需要维护前500就好了。。。。
然后还有 i 到除了i 其兄弟以及兄弟子树的 dis=k,我们再用一个dfs 解决即可,不过会重复计算,我们算两遍 然后/2即可。
using namespace std; #define ll __int64 /* */ vector<int>G[50005]; ll dp[50005][505],ans; int n,k; void dfs(int u,int pre){ dp[u][0]=1; for(int i=0;i<G[u].size();i++){ int to=G[u][i]; if(to==pre) continue; dfs(to,u); for(int j=1;j<=k;j++){ dp[u][j]+=dp[to][j-1]; // printf("dfs1:%d %d %d\n",u,j,dp[u][j]); } } } void dfs2(int u,int pre){ for(int i=0;i<G[u].size();i++){ int to=G[u][i]; if(to==pre) continue; for(int j=0;j<=k;j++){ // dp[to][j] dis=j + dp[u][x] = k x=k-1-j int x=k-1-j; if(x<=0) continue; //避免和祖先重复计算 dp[u][x] -=dp[to][x-1]; //删掉属于u子树的 ans+=dp[to][j] * dp[u][x]; //兄弟之间是重复计算的 // printf("%d %d x,j :%d %d dp:%I64d %I64d\n",u,to, x,j,dp[u][x],dp[to][j]); dp[u][x] +=dp[to][x-1]; //.... } dfs2(to,u); } } int main() { //freopen("1.txt","r",stdin); scanf("%d %d",&n,&k); int u,v; for(int i=1;i<n;i++){ scanf("%d %d",&u,&v); G[u].push_back(v); G[v].push_back(u); } dfs(1,0); ans=0; for(int i=1;i<=n;i++) ans+=dp[i][k]*2; dfs2(1,0); printf("%I64d\n",ans/2); return 0; }回想起 这道题有很多变形,我打算特意找一下这道题的变形: