[湖南集訓]談笑風生
阿新 • • 發佈:2018-12-06
[湖南集訓]談笑風生
這題有很多方法做,一堆大佬用線段樹合併、主席樹來做,但是我太弱了,只會長鏈剖分。
長鏈剖分的解法,思維簡單,碼量極低,是你的不二之選!
還是老規矩,先上\(n^2\)DP。
題意就不說了,但是先注意到,如果\(b\)在\(a\)的上方,\(c\)直接取\(a\)子樹中除了\(a\)的所有點就好了,DP的目的只是統計\(b\)在\(a\)的下方的情況。
首先設\(f[i][j]\)表示如果把\(a\)取在\(i\)點,\(b\)取在以\(i\)為根的子樹中距離\(a\)為\(j\)的點上的方案數。特別地,\(f[i][0]\)表示把\(b\)取在\(a\)
可以寫出轉移方程:\(f[i][j+1]=\sum f[t][i]\),其中\(t\)是\(i\)的子節點。
觀察一下題目要求的東西,發現需要對\(f[i][j]\)求個字首和(意思是對於同一個\(i\),求\(j\)變化時的字首和),然而字首和不方便,那就求字尾和。
所以我們的\(f[i][j]\)的意義需要在原來的基礎上做一些調整,變成原來意義上的字尾和。方程沒有變:\(f[i][j+1]=\sum f[t][i]\),只是加上一句\(f[i][0]+=f[t][0]\)
DP完一個點之後就統計答案,很簡單就不贅述了。
但是上面這個DP是\(O(n^2)\)的,由於下標是深度,套個長鏈剖分的板子就好了。不會長鏈剖分的同學可以看看我的長鏈剖分總結。
#include<cstdio> #include<cctype> #include<algorithm> #include<vector> #define R register #define I inline using namespace std; const int S=300003,N=600003; char buf[S],*p1,*p2; I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;} I int rd(){ R int f=0; R char c=gc(); while(c<48||c>57) c=gc(); while(c>47&&c<58) f=f*10+(c^48),c=gc(); return f; } int h[S],s[N],g[N],d[S],w[S],t[S],p[S],q[S],c; long long *f[S],u[S],o[S],*e=u+1; vector<pair<int,int> > v[S]; I int min(int x,int y){return x<y?x:y;} I void add(int x,int y){s[++c]=h[x],g[c]=y,h[x]=c;} I void ini(int x){f[x]=e,e+=t[x]-d[x]+1;} void dfs1(int x,int f){t[x]=d[x]=d[f]+1,p[x]=f,w[x]=1; for(R int i=h[x],y;i;i=s[i]) if((y=g[i])^f){dfs1(y,x),w[x]+=w[y]; if(t[y]>t[x]) t[x]=t[y],q[x]=y; } } void dfs2(int x){f[x][0]=w[x]-1; if(q[x]) f[q[x]]=f[x]+1,dfs2(q[x]),f[x][0]+=f[q[x]][0]; R int i,j,k,y,m; for(i=h[x];i;i=s[i]) if((y=g[i])^p[x]&&y^q[x]) for(j=0,ini(y),dfs2(y),f[x][0]+=f[y][0],m=t[y]-d[y];j<=m;++j) f[x][j+1]+=f[y][j]; for(i=0,m=v[x].size()-1;i<=m;++i){ k=v[x][i].first,j=v[x][i].second,o[j]+=1ll*(w[x]-1)*min(d[x]-1,k); if(k>=t[x]-d[x]) o[j]+=f[x][0]-w[x]+1; else o[j]+=f[x][0]-w[x]+1-f[x][k+1]; } } int main(){ R int n=rd(),m=rd(),i,x,y; for(i=1;i<n;++i) x=rd(),y=rd(),add(x,y),add(y,x); for(dfs1(1,0),i=1;i<=m;++i) x=rd(),y=rd(),v[x].push_back(make_pair(y,i)); for(ini(1),dfs2(1),i=1;i<=m;++i) printf("%lld\n",o[i]); return 0; }