EEE
路径
https://ac.nowcoder.com/acm/contest/10743/E
题目中问到关于所有路径的问题,这样我们很自然就能想到需要通过点分治来解决此类问题(我跟你讲淀粉质可好吃了),然后我们需要进一步解决的就是如何快速统计之后的答案,题目中出题人很良心的告诉了∑z的数据范围1≤∑z≤2×10^5所以我可以统计一个前缀和然后二分查找答案(zz的storm竟然写了一个treap,然后T了),这个二分可以通过STL的lower_bound实现,然后我们这样写就过了。所以这道题实质上是一道非常好的点分治的模板题。
经过出题人的提醒,我才意识到这个做法是错误的。对于一个菊花图来说,如果用一个桶来更新的话,我们的更新复杂度可能会被卡到S^2(S=路径的个数),这样对于这个题来说是不可以通过的,那么我们就需要来优化复杂度,通过观察我们统计答案的步骤我们可以,得到一个a[]数组,角标i就是表示路径长度为i的个数,这样我们就可以用每次getdis()更新的路径与他卷积之后就可以更新答案,最后统计答案的做法不变。经过我的不懈努力终于写了出来,提交之后会发现T掉了最后10%的数据,在看了题解区dalao的评论之后,我发现这个做法并不会是官方题解给出复杂度严格SlogS如果一直统计较大的路径这种做法也会劣化,我们采取的做法是由路径长度由小到大更新。
最后吐槽一句,这个题不卡N^2暴力不卡裸的点分治,而且正解NTT竟然会比N^2暴力还慢?
#include<cstdio> #include<set> #include<iostream> #include<ctime> #include<cmath> #include<algorithm> using namespace std; #define maxn 1000010 #define maxs 1000010 #define INF 0x3f3f3f3f const int mod = 1e9+7; const double pie=acos(-1); namespace IO { template <typename T> inline void w(T x) { if (x > 9) w(x / 10); putchar(x % 10 + 48); } template <typename T> inline void write(T x, char c) { if(x < 0) putchar('-'), x = -x; w(x); putchar(c); } template <typename T> inline void read(T &x) { x = 0; T f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -1; for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48); x *= f; } }; int e[maxn],ne[maxn],h[maxn],cst[maxn],bit,dis[maxn],idx=1,sz[maxn],maxx[maxn],sum,root,ans[maxs],rev[maxs],lena,lenb,a[maxs],b[maxs],tot,maxdep; bool vis[maxn]; void add(int a,int b,int c){ e[idx]=b;ne[idx]=h[a];cst[idx]=c;h[a]=idx++; e[idx]=a;ne[idx]=h[b];cst[idx]=c;h[b]=idx++; } struct cp{ double real,imaginary; cp operator +(cp that){ return (cp){real+that.real,imaginary+that.imaginary}; } cp operator -(cp that){ return (cp){real-that.real,imaginary-that.imaginary}; } cp operator *(cp that){ return (cp){real*that.real-imaginary*that.imaginary,imaginary*that.real+real*that.imaginary}; } }f[maxn],g[maxn]; void FFT(cp *F,int len,int flag){ for(int i=1;i<len;i++){ rev[i]=(rev[i>>1]>>1|(i&1)<<bit); if(i<rev[i]) swap(F[i],F[rev[i]]); } for(int i=1;i<len;i<<=1){ cp x,y,wn={cos(pie/i),flag*sin(pie/i)}; for(int j=0,add=i<<1;j<len;j+=add){ cp w=(cp){1,0}; for(int k=0;k<i;k++,w=w*wn){ x=F[j+k];y=w*F[i+j+k]; F[j+k]=x+y;F[i+j+k]=x-y; } } } return; } void work(int n,int m){ int len=1; bit=0; while(len<=n+m){ len<<=1; bit++; }bit--; for(int i=0;i<=n;i++) f[i].real=(double)a[i]; for(int i=0;i<=m;i++){ g[i].real=(double)b[i]; a[i]+=b[i]; b[i]=0; } lena=max(n,m); FFT(f,len,1);FFT(g,len,1); for(int i=0;i<=len;i++) f[i]=f[i]*g[i]; FFT(f,len,-1); for(int i=0;i<=n+m;i++) ans[i]+=(int)(f[i].real/len+0.5); for(int i=0;i<len;i++) f[i].real=f[i].imaginary=g[i].real=g[i].imaginary=0.0; return; } void getroot(int u,int fa){ sz[u]=1; maxx[u]=0; for(int i=h[u];i;i=ne[i]){ if(e[i]==fa||vis[e[i]]) continue; getroot(e[i],u); sz[u]+=sz[e[i]]; maxx[u]=max(maxx[u],sz[e[i]]); } maxx[u]=max(maxx[u],sum-maxx[u]); if(maxx[root]>maxx[u]){ root=u; } return; } void getdis(int u,int fa){ b[dis[u]]++; if(lenb<dis[u]) lenb=dis[u]; for(int i=h[u];i;i=ne[i]){ if(e[i]==fa||vis[e[i]]) continue; dis[e[i]]=dis[u]+cst[i]; getdis(e[i],u); } } struct Son { int v,c,d; bool operator<(const Son &b) const { return d < b.d; } } sons[maxn]; void getdep(int u,int fa,int dep){ maxdep=max(maxdep,dep); for(int i=h[u];i;i=ne[i]){ if(vis[u]||e[i]==fa) continue; getdep(e[i],u,dep+cst[i]); } } void clac(int u){ a[0]=1;lena=1;tot=0; for(int i=h[u];i;i=ne[i]){ if(vis[e[i]]) continue; maxdep=0; getdep(e[i],u,cst[i]); sons[tot++]=(Son){e[i],cst[i],maxdep}; } sort(sons,sons+tot); for(int i=0;i<tot;i++){ int v=sons[i].v,w=sons[i].c; lenb=-1; dis[v]=w; getdis(v,u); work(lena,lenb); } for(int i=0;i<=lena;i++){ a[i]=0; } return; } void solve(int u){ vis[u]=true; clac(u); for(int i=h[u];i;i=ne[i]){ if(vis[e[i]]) continue; sum=sz[e[i]]; root=0; getroot(e[i],0); solve(root); } return; } using namespace IO; int main(){ srand(time(0)); int n,m,x,y,z,k; IO::read(n); read(m); for(int i=1;i<n;i++){ read(x); read(y); read(z); add(x,y,z); } sum=n; root=0; maxx[0]=INF; getroot(1,0); solve(root); for(int i=1;i<=200000;i++){ ans[i]+=ans[i-1]; } while(m--){ read(k); k=lower_bound(ans,ans+200001,k)-ans; printf("%d\n",k); } return 0; }