标签:中心 continue 包含 情况 void 离线 date play pop
来一个O(nlogn)做法,跑得挺快。
假设点集S包含点X,Y,Z,红色链长度为a,黄色链长度为b,绿色链长度为c。
如果|a-b|>1,那么有|dis(X,Z)-dis(Y,Z)|=|(a+c)-(b+c)|=|a-b|>1,不符合题目要求。
所以a=b或|a-b|=1。所以存在一个点A,到S中每一个点的距离都为p或p+1(0<2p<n)。
接着发现一定是一下两种情况之一(证明留给读者自行思考):
情况1:S中任两点到点A的链没有公共边。
情况2:S中任意一点P满足min(dis(P,A),dis(P,A‘))=p(A‘为一个与A相邻的点)。
于是,S中的点便是由A点或者A点及A‘点伸出来的若干条“长度相近”链的另一端点组成(如图)。
其中“长度相近”指长度均为p或p+1,且长度为p的链只有一条或长度为p+1的链只有一条。
预处理出每一个点连出所有边引出链的最大长度,对于一个点P,设其引出第k长的链长度为len(p,k),则$ans_{len(p,k)} \geq k$。
又因为$ans_x \geq ans_{x+2}$,所以可以先对于每个k,求出最大的x使$ans_x \geq k$,再做一边后缀最大值。
先枚举其中一个中心点,再枚举其引出的一条边,设k为这条边引出链的最大长度,一共可以引出d条长度为k的链。
那么执行ans[k]=max(ans[k],d)。对每个点,将其引出链按最大长度排序即可求得d和k。
这样一个中心点的情况就搞定了。
对于点i,设集合S_i中有deg_i个元素,分别为i引出deg_i条边所引出链长度的最大值。
枚举其中一个中心点P,设另一个为Q,则P,Q相邻。我们只需要对于每一个$S_{P,j}(0 \leq j<deg_P)$,找到点Q,使集合S_P,S_Q中大于$S_{P,j}$的数最多。因为每个数相当于一条链的长度。把问题离线下来(按照$S_{P,j}$从大到小排序),用bfs序把与P相邻的点转化为一段区间以及fa_P,然后写一个支持单点加,区间最大值的线段树即可。具体见代码吧。
#include<queue> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,cnt=-1,temp,qcnt=0; int deg[500000],l1[500000],l2[500000]; int fir[500000],to[1000000],nxt[1000000]; int fa[500000],ans[500000],f[500000]; int id[500000],mx[2000000],o[500000]; vector<int> len[500000]; struct query{ int x,val,o; }qu[1000000]; inline bool comp(query q1,query q2) { return q1.val>q2.val; } inline void Add(int a,int b,int c) { qu[qcnt].x=a; qu[qcnt].val=c; qu[qcnt++].o=b; return; } inline void add(int a,int b) { to[++cnt]=b; nxt[cnt]=fir[a]; fir[a]=cnt; deg[b]++; return; } void dfs1(int i,int f) { fa[i]=f; for(int j=fir[i];j!=-1;j=nxt[j]){ if(to[j]==f) continue; dfs1(to[j],i); l1[i]=max(l1[i],l1[to[j]]); } l1[i]++; return; } void dfs2(int i,int l) { l2[i]=l; int mx1=0,mx2=0; for(int j=fir[i];j!=-1;j=nxt[j]){ temp=to[j]; if(temp==fa[i]) continue; if(mx1<l1[temp]){ mx2=mx1; mx1=l1[temp]; } else if(mx2<l1[temp]) mx2=l1[temp]; } for(int j=fir[i];j!=-1;j=nxt[j]){ temp=to[j]; if(temp==fa[i]) continue; if(mx1==l1[temp]) dfs2(temp,max(mx2,l2[i])+1); else dfs2(temp,max(mx1,l2[i])+1); } return; } int t1,t2; void upd(int i,int l,int r) { if(l==r){ mx[i]++; return; } int mid=(l+r)>>1; if(t1<=mid) upd(i<<1,l,mid); else upd(i<<1|1,mid+1,r); mx[i]=max(mx[i<<1],mx[i<<1|1]); return; } inline void update(int x) { t1=x; upd(1,0,n-1); f[x]++; return; } int qry(int i,int l,int r) { if(t1<=l&&r<=t2) return mx[i]; int mid=(l+r)>>1,res=0; if(t1<=mid) res=qry(i<<1,l,mid); if(mid<t2) res=max(res,qry(i<<1|1,mid+1,r)); return res; } inline int querymax(int x,int y) { t1=x; t2=y; return qry(1,0,n-1); } void bfs(void) { cnt=0; id[0]=cnt++; queue<int> q; q.push(0); while(q.size()>0){ int i=q.front(); q.pop(); o[i]=cnt-1; for(int j=fir[i];j!=-1;j=nxt[j]) if(to[j]!=fa[i]){ id[to[j]]=cnt++; q.push(to[j]); } } return; } void solve(void) { for(int i=2;i<n;i++) ans[i]=1; for(int i=0;i<n;i++){ int s=deg[i]; for(int d=1;d<=s;d++){ temp=len[i][s-d]*2; if(temp<n) ans[temp]=max(ans[temp],d); if(temp<=n) ans[temp-1]=max(ans[temp-1],d); if(temp+1<n&&d>1&&len[i][s-d+1]>len[i][s-d]) ans[temp+1]=max(ans[temp+1],d); } } for(int i=0;i<n;i++) for(int d=1;d<=deg[i];d++) Add(i,d,len[i][deg[i]-d]); sort(qu,qu+qcnt,comp); int pos=0; for(int i=0;i<qcnt;i++){ while(pos<qcnt&&qu[pos].val>=qu[i].val) update(id[qu[pos++].x]); int X=qu[i].x,D=2*qu[i].val; temp=querymax(o[X]+1,o[X]+deg[X]-1); if(X!=0) temp=max(temp,f[id[fa[X]]]); ans[D]=max(ans[D],temp+qu[i].o-2); } return; } int main(void) { scanf("%d",&n); for(int i=0;i<n;i++) fir[i]=-1; int a,b; for(int i=1;i<n;i++){ scanf("%d%d",&a,&b); add(a-1,b-1); add(b-1,a-1); } dfs1(0,-1); dfs2(0,0); bfs(); for(int i=1;i<n;i++){ len[fa[i]].push_back(l1[i]); len[i].push_back(l2[i]); } for(int i=0;i<n;i++){ sort(len[i].begin(),len[i].end()); ans[1]=max(ans[1],deg[i]+1); } solve(); for(int i=n-3;i>0;i--) ans[i]=max(ans[i],ans[i+2]); for(int i=1;i<n;i++) printf("%d ",ans[i]); printf("1\n"); return 0; }
标签:中心 continue 包含 情况 void 离线 date play pop
原文地址:https://www.cnblogs.com/2005lz/p/13121667.html