题目大概说一棵树,树边有费用和收益两个属性,求一条收益和最大的路径满足费用和不超过C。
树上任意两点的路径都可以看成是过某一个子树根的路径,显然树分治。
治的时候要解决的一个问题是,找到费用小于等于某个数且收益最大的值。
这个很容易想到用线段树,不过不想写线段树。。
想了想,想到可以先排序,从小到大去找,之前找到哪现在就继续从那儿开始找,这样最多也就遍历一遍待查找数组,具体看代码。
两次排序占大头,最后时间复杂度是O(nlog2n)。
WA了一次,因为只考虑了两端都过根的路径,忽略了一端点是根的路径。。之前写树分治也是因为这个WA。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define INF (1<<30) 6 #define MAXN 22222 7 struct Edge{ 8 int v,b,c,next; 9 }edge[MAXN<<1]; 10 int NE,head[MAXN]; 11 void addEdge(int u,int v,int b,int c){ 12 edge[NE].v=v; edge[NE].b=b; edge[NE].c=c; edge[NE].next=head[u]; 13 head[u]=NE++; 14 } 15 bool vis[MAXN]; 16 int size[MAXN]; 17 void getsize(int u,int fa){ 18 size[u]=1; 19 for(int i=head[u]; i!=-1; i=edge[i].next){ 20 int v=edge[i].v; 21 if(v==fa || vis[v]) continue; 22 getsize(v,u); 23 size[u]+=size[v]; 24 } 25 } 26 int mm,cen; 27 void getcen(int u,int fa,int &tot){ 28 int res=tot-size[u]; 29 for(int i=head[u]; i!=-1; i=edge[i].next){ 30 int v=edge[i].v; 31 if(v==fa || vis[v]) continue; 32 getcen(v,u,tot); 33 res=max(res,size[v]); 34 } 35 if(res<mm){ 36 mm=res; 37 cen=u; 38 } 39 } 40 int getcen(int u){ 41 getsize(u,u); 42 mm=INF; 43 getcen(u,u,size[u]); 44 return cen; 45 } 46 struct Rec{ 47 int b,c; 48 bool operator<(const Rec &r)const{ 49 return c<r.c; 50 } 51 }ra[MAXN],rb[MAXN]; 52 int tot,an,bn; 53 void dfs(int u,int fa,int benfit,int cost){ 54 rb[bn].b=benfit; 55 rb[bn].c=cost; 56 bn++; 57 for(int i=head[u]; i!=-1; i=edge[i].next){ 58 int v=edge[i].v; 59 if(v==fa || vis[v]) continue; 60 dfs(v,u,benfit+edge[i].b,cost+edge[i].c); 61 } 62 } 63 int ans; 64 void conqur(int u){ 65 an=1; 66 ra[0].b=0; ra[0].c=0; 67 for(int i=head[u]; i!=-1; i=edge[i].next){ 68 int v=edge[i].v; 69 if(vis[v]) continue; 70 bn=0; 71 dfs(v,v,edge[i].b,edge[i].c); 72 sort(ra,ra+an); 73 sort(rb,rb+bn); 74 int pa=an-1,pb=0; 75 while(pb<bn){ 76 int mx=-1; 77 for(int j=pa; j>=0; --j){ 78 if(ra[j].c+rb[pb].c<=tot){ 79 if(mx<ra[j].b+rb[pb].b){ 80 mx=ra[j].b+rb[pb].b; 81 pa=j; 82 } 83 } 84 } 85 if(mx==-1) break; 86 ans=max(ans,mx); 87 ++pb; 88 } 89 for(int j=0; j<bn; ++j){ 90 ra[an++]=rb[j]; 91 } 92 } 93 } 94 void divide(int u){ 95 u=getcen(u); 96 vis[u]=1; 97 conqur(u); 98 for(int i=head[u]; i!=-1; i=edge[i].next){ 99 int v=edge[i].v; 100 if(vis[v]) continue; 101 divide(v); 102 } 103 } 104 int main(){ 105 int t,n,a,b,c,d; 106 scanf("%d",&t); 107 while(t--){ 108 scanf("%d",&n); 109 NE=0; 110 memset(head,-1,sizeof(head)); 111 for(int i=1; i<n; ++i){ 112 scanf("%d%d%d%d",&a,&b,&c,&d); 113 addEdge(a,b,d,c); 114 addEdge(b,a,d,c); 115 } 116 scanf("%d",&tot); 117 ans=0; 118 memset(vis,0,sizeof(vis)); 119 divide(1); 120 printf("%d\n",ans); 121 } 122 return 0; 123 }
时间: 2024-10-12 19:42:33