bzoj2067: [Poi2004]SZN
一开始没看出来是贪心,还以为是树规,多亏ooo提醒一句,然后刚了一个半小时搞出来了。
首先‘最长线最短’二分没错了,想了想他确实是单调的,最长线越长,用的线就越短(注意这里的最长线只是不超过,并不是一定要达到)。
二分最长线长度,对于已知的最长线长度len,考虑如何求解最少线数。
树里有什么特殊的点吗?叶子节点,叶子节点一定是一条线的一个端点。所以从叶子节点开始,一条线最优肯定是直接连接两个叶子节点,证明自己YY(其实是我不会)。
设f[i]表示节点i向上的线的长度。显然叶子节点的f为1。对于非叶子节点考虑如何合并:
f相加<=len的两个儿子可以合并,但并不是随便合并就是最优的。可以将儿子存到一个队列里,按f值从小到大排序,用两个指针实现合并。如果f[head]+f[tail]<=len直接合并head++,tail--,ans++。如果大于len直接把tail独立成一条线吗?这样并不是最优的,可以把tail的值记录加到f[x]到上一层合并。但是只能有一个点和x接上,显然选f最小的,其他的点就只能独立成一条线了。注意最后如果head==tail要特判是和x接上还是独立成一条线。
这样直接合并到根就求出了最长线长度为len时的最少线数。
复杂度我不会算,有点玄但是跑的还挺快。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 //19.00~20.00 :93 2 //20.00~20.25 :100 3 #include<algorithm> 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #define int LL 8 #define MAXN 20010 9 #define LL long long 10 #define ma(x) memset(x,0,sizeof(x)) 11 using namespace std; 12 struct edge 13 { 14 int u,v,nxt; 15 #define u(x) ed[x].u 16 #define v(x) ed[x].v 17 #define n(x) ed[x].nxt 18 }ed[MAXN*4]; 19 int first[MAXN],num_e; 20 #define f(x) first[x] 21 const int INF=0x7ffffffffff; 22 int n,tans; 23 int f[MAXN]; 24 25 pair<int,int> q[MAXN];int head,tail; 26 void dfs(int x,int fa,int len) 27 { 28 int tem=0; 29 for(int i=f(x);i;i=n(i)) 30 if(v(i)!=fa){dfs(v(i),x,len);} 31 32 f[x]=0;if(x!=1)f[x]++;head=1,tail=0;int ooo=INF; 33 for(int i=f(x);i;i=n(i)) 34 if(v(i)!=fa) 35 if(f[v(i)]==len)tans++; 36 else{q[++tail]=make_pair(f[v(i)],v(i));} 37 sort(q+head,q+tail+1); 38 while(head<tail) 39 { 40 if(q[head].first+q[tail].first>len) 41 { 42 if(q[tail].first<ooo) 43 { 44 if(ooo!=INF)tans++; 45 ooo=q[tail].first;tail--; 46 } 47 else tans++,tail--; 48 } 49 else head++,tail--,tans++; 50 } 51 if(head==tail) 52 { 53 if(q[tail].first<ooo) 54 { 55 if(ooo!=INF)tans++; 56 ooo=q[tail].first; 57 } 58 else tans++; 59 } 60 if(ooo!=INF)f[x]+=ooo; 61 } 62 int solve(int len) 63 { 64 ma(f);tans=0; 65 dfs(1,0,len); 66 if(f[1])tans++; 67 return tans; 68 } 69 inline int read(); 70 inline void add(int u,int v); 71 signed main() 72 { 73 // freopen("in.txt","r",stdin); 74 //freopen("1.out","w",stdout); 75 76 n=read();int ta,tb; 77 for(int i=1;i<n;i++) 78 ta=read(),tb=read(), 79 add(ta,tb),add(tb,ta); 80 int l=1,r=n,mid,ans=solve(n),now=INF; 81 int ans1,ans2; 82 while(l<=r) 83 { 84 mid=(l+r)>>1; 85 int res=solve(mid); 86 if(res>ans)l=mid+1; 87 else 88 { 89 r=mid-1,ans=res; 90 ans1=mid;ans2=res; 91 } 92 } 93 printf("%lld %lld\n",ans2,ans1); 94 } 95 inline int read() 96 { 97 int s=0,f=1;char a=getchar(); 98 while(a<'0'||a>'9'){if(a=='-')f=-1;a=getchar();} 99 while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();} 100 return s*f; 101 } 102 inline void add(int u,int v) 103 { 104 ++num_e; 105 u(num_e)=u; 106 v(num_e)=v; 107 n(num_e)=f(u); 108 f(u)=num_e; 109 }