noip2015 运输计划
https://www.luogu.org/problemnew/show/P2680
首先预处理出每个计划的原时间,然后把计划按时间从小到大排序
显然,最优方案一定是选某一个计划i,记路径集合S为i以及排序后在i后面的所有计划的路径,找出S中所有路径的交集(如果交集为空则无答案),取交集中所有边上长度的最大值(交集显然一定也是一条可以只用两个端点来表示的路径),这样对于S中所有路径,都可以去掉这条边,因此此时答案为max(最长一条路径所需时间-这个最大值,排序后的第i-1条路径所需时间)
因此如果能求路径交集,那么只要按时间从大到小扫一遍即可
然后我不会求路径交。。。
搜了一下,有两种,第一种是暴力求出要求路径交的两个点对(a,b)与(c,d)共4个点之间的6对lca,显然路径交的两个端点都在这6个点中,那么枚举两个端点(x,y)然后取合法(就是满足x,y都分别在(a,b)路径上与(c,d)路径上)且(x,y)间距离最长的作为答案即可
然后超大常数、超大代码量、超大调试量、超丑代码等着我。。。。
(好像还有一种是把每条路径拆成深度单调的两条,对于四条路径两两求交,然后并起来?更麻烦的样子啊)
曾经错误:
1.22行maxn[u][i]=max(maxn[u][i-1],maxn[anc[i][i-1]][i-1]);
2.58行max(ans,maxn[a][0])【老错误了】
3.85行的||打成&&
1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 #define pb push_back 5 #define fi first 6 #define se second 7 using namespace std; 8 struct E 9 { 10 int to,nxt,d; 11 }e[600100]; 12 int f1[300100],ne,anc[300100][30],l2n=19,dep[300100],dd[300100]; 13 int maxn[300100][30]; 14 int n,m; 15 int lft[40]; 16 void dfs1(int u,int fa) 17 { 18 int i,k; 19 for(i=1;i<=l2n;i++) 20 { 21 anc[u][i]=anc[anc[u][i-1]][i-1]; 22 maxn[u][i]=max(maxn[u][i-1],maxn[anc[u][i-1]][i-1]); 23 } 24 for(k=f1[u];k;k=e[k].nxt) 25 if(e[k].to!=fa) 26 { 27 dep[e[k].to]=dep[u]+1; 28 dd[e[k].to]=dd[u]+e[k].d; 29 anc[e[k].to][0]=u; 30 maxn[e[k].to][0]=e[k].d; 31 dfs1(e[k].to,u); 32 } 33 } 34 int lca(int a,int b) 35 { 36 if(dep[a]<dep[b]) swap(a,b); 37 int i,t=dep[a]-dep[b]; 38 for(i=l2n;i>=0;i--) 39 if(lft[i]<=t) 40 a=anc[a][i],t-=lft[i]; 41 if(a==b) return a; 42 for(i=l2n;i>=0;i--) 43 if(anc[a][i]!=anc[b][i]) 44 a=anc[a][i],b=anc[b][i]; 45 return anc[a][0]; 46 } 47 int getmax(int a,int b) 48 { 49 if(dep[a]<dep[b]) swap(a,b); 50 int i,t=dep[a]-dep[b],ans=0; 51 for(i=l2n;i>=0;i--) 52 if(lft[i]<=t) 53 ans=max(ans,maxn[a][i]),a=anc[a][i],t-=lft[i]; 54 if(a==b) return ans; 55 for(i=l2n;i>=0;i--) 56 if(anc[a][i]!=anc[b][i]) 57 ans=max(ans,maxn[a][i]),ans=max(ans,maxn[b][i]),a=anc[a][i],b=anc[b][i]; 58 return max(ans,max(maxn[a][0],maxn[b][0])); 59 } 60 int getdis(int a,int b) 61 { 62 return dd[a]+dd[b]-2*dd[lca(a,b)]; 63 } 64 struct Q 65 { 66 int a,b,d; 67 }q[300100]; 68 bool operator<(const Q &a,const Q &b) {return a.d<b.d;} 69 int k_anc(int a,int k)//a的k级祖先 70 { 71 int i; 72 for(i=l2n;i>=0;i--) 73 if(lft[i]<=k) 74 a=anc[a][i],k-=lft[i]; 75 return a; 76 } 77 bool isanc(int a,int b)//b是否是a或a的祖先 78 { 79 if(dep[b]>dep[a]) return 0; 80 return k_anc(a,dep[a]-dep[b])==b; 81 } 82 typedef pair<int,int> P; 83 bool isin(int a,const P &b)//点a是否在点对b间路径上 84 { 85 return (isanc(b.fi,a)||isanc(b.se,a))&&!isanc(anc[lca(b.fi,b.se)][0],a); 86 } 87 bool isin(const P &a,const P &b)//点对a间路径是否被包含在点对b间路径中 88 { 89 return isin(a.fi,b)&&isin(a.se,b); 90 } 91 P get(const P &a,const P &b) 92 { 93 int tmp[]={ 94 lca(a.fi,a.se),lca(a.fi,b.fi),lca(a.fi,b.se), 95 lca(a.se,b.fi),lca(a.se,b.se),lca(b.fi,b.se), 96 }; 97 bool ok=0;int i,j,dd,dt;P t,ans(0,0); 98 for(i=0;i<6;i++) 99 for(j=i+1;j<6;j++) 100 { 101 t=P(tmp[i],tmp[j]); 102 if(isin(t,a)&&isin(t,b)) 103 { 104 if(!ok) 105 { 106 ok=1;ans=t; 107 dd=dep[t.fi]+dep[t.se]-2*dep[lca(t.fi,t.se)]+1; 108 } 109 else 110 { 111 dt=dep[t.fi]+dep[t.se]-2*dep[lca(t.fi,t.se)]+1; 112 if(dt>dd) dd=dt,ans=t; 113 } 114 } 115 } 116 return ans; 117 } 118 int anss; 119 int main() 120 { 121 int i,a,b,d;lft[0]=1; 122 for(i=1;i<=l2n;i++) lft[i]=lft[i-1]<<1; 123 scanf("%d%d",&n,&m); 124 for(i=1;i<n;i++) 125 { 126 scanf("%d%d%d",&a,&b,&d); 127 e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].d=d; 128 e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].d=d; 129 } 130 dfs1(1,0); 131 for(i=1;i<=m;i++) 132 { 133 scanf("%d%d",&q[i].a,&q[i].b); 134 q[i].d=getdis(q[i].a,q[i].b); 135 } 136 sort(q+1,q+m+1); 137 //printf("a%d\n",getmax(3,4)); 138 P now=P(q[m].a,q[m].b);anss=max(q[m-1].d,q[m].d-getmax(q[m].a,q[m].b)); 139 for(i=m-1;i>=1;i--) 140 { 141 now=get(now,P(q[i].a,q[i].b)); 142 if(now==P(0,0)) break; 143 anss=min(anss,max(q[i-1].d,q[m].d-getmax(now.first,now.second))); 144 } 145 printf("%d",anss); 146 return 0; 147 }
当然还有一种方法:用树上差分显然可以O(n+m)求路径交(就是统计每个点被路径经过了几次,然后被经过次数与路径数相等的就属于路径交)
注意到答案可以二分,然后就完了。。。。。
。。。。代码以后再说吧