E. ALT
http://codeforces.com/problemset/problem/786/E
题意:
给出一棵 n 个节点的树与 m 个工人。
每个工人有一条上下班路线(简单路径),一个工人会得到满足只要下面一项满足:
1、他得到一只puppy
2、他的上下班路径上每条边都有一只doge
求使所有工人满足的最小dog数并输出方案。n≤20000,m≤10000
想法:暴力建图,源点连向工人Xi,边Yi连向汇点,容量为1。然后工人向其路径上的边连容量为1的边。O(nm)
优化建图:路径为树上连续路径,那么可以套上树链剖分变成连续区间,再用线段树优化连边....O(mlog^2n)。
反正不带修改,那么求树上路径问题还可以用点分治和倍增。O(mlogn)
点分治:对于同一个重心,维护一条轨道,通过轨道可以到达这个点到重心所有点。
具体:add(D[i],Y[i]),add(D[i],D[i-1]);
倍增:可以形象成树上一条链被拆分成若干个区间,步长为2^j,大区间向其包含的小区间连边,这个类似线段树优化建边。具体:add(G[j][x],G[j-1][x]),add(G[j][x],G[j-1][ F[j-1][x] ]);
最小割方案的输出:对于Xi如果划分到{T}集合,那么他与源点的边就被割了,他得到了一只puppy。如果Yi被划分到{S},那么它与汇点的边被割了,一只doge放在这条边上。
附上最小割+倍增的代码(点分治好麻烦....懒癌晚期患者表示不想写)
#include<cstdio> #include<cmath> #include<vector> const int len(20000),N(400000),INF(0x7fffffff); struct Node{int nd,nx,pos;}bot[len*2+10];int tot,first[len+10],v,u; int f[16][len+10],g[16][len+10],logg,depth[len+10],pos[len+10]; struct Data{int nd,fl,nx;}; std::vector<Data>Edge[N+10];int S,T; int n,m,ans,now,last,st[len+10],top,flag[N+10];; int min(int a,int b){return a>b?b:a;} void swap(int &a,int &b){if(a==b)return;a^=b;b^=a;a^=b;} void add(int a,int b,int p){bot[++tot]=(Node){b,first[a],p};first[a]=tot;} void addedge(int a,int b,int f) { Edge[a].push_back((Data){b,f,(int)Edge[b].size()}); Edge[b].push_back((Data){a,0,(int)Edge[a].size()-1}); } template <class T>void read(T &x) { x=0;char ch=getchar(); while(ch<‘0‘||ch>‘9‘)ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} } void update(int x) { for(int j=1;j<=logg;j++) { f[j][x]=f[j-1][ f[j-1][x] ]; g[j][x]=++now; addedge(g[j][x],g[j-1][x],INF); addedge(g[j][x],g[j-1][ f[j-1][x] ],INF); } } void Dfs(int x,int fa) { f[0][x]=fa; g[0][x]=x; update(x); depth[x]=depth[fa]+1; for(int v=first[x];v;v=bot[v].nx) if(bot[v].nd!=fa) pos[bot[v].nd]=bot[v].pos,Dfs(bot[v].nd,x); } int lca(int u,int v) { if(depth[u]<depth[v])swap(u,v); for(int k=depth[u]-depth[v],j=0;k;k>>=1,j++) if(k&1)u=f[j][u]; if(u==v)return u; for(int j=logg;j>=0&&f[0][v]!=f[0][u];j--) if(f[j][u]!=f[j][v])u=f[j][u],v=f[j][v]; return f[0][v]; } void link(int x,int y,int t) { for(int k=depth[x]-depth[t],j=0;k;k>>=1,j++)//LCA不选 if(k&1)addedge(now,g[j][x],INF),x=f[j][x]; for(int k=depth[y]-depth[t],j=0;k;k>>=1,j++)//LCA不选 if(k&1)addedge(now,g[j][y],INF),y=f[j][y]; } void DFS_Put(int x) { flag[x]=1; for(int v=0,sz=Edge[x].size();v<sz;v++) { Data y=Edge[x][v]; if(y.fl&&!flag[y.nd])DFS_Put(y.nd); } } int q[N+10],l,h,dis[N+10]; bool bfs(int s,int t) { for(int i=1;i<=T;i++)dis[i]=INF; q[l=1]=s; h=0; dis[s]=0; while(h<l) { int x=q[++h]; for(int v=0,sz=Edge[x].size();v<sz;v++) { Data y=Edge[x][v]; if(y.fl&&dis[y.nd]==INF) dis[y.nd]=dis[x]+1,q[++l]=y.nd; } } return dis[t]!=INF; } int dfs(int x,int t,int flow) { if(x==t)return flow; int sum=0,tmp; for(int v=0,sz=Edge[x].size();v<sz;v++) { Data y=Edge[x][v]; if(y.fl&&dis[y.nd]==dis[x]+1) { tmp=dfs(y.nd,t,min(y.fl,flow)); flow-=tmp; sum+=tmp; Edge[x][v].fl-=tmp; Edge[y.nd][y.nx].fl+=tmp; if(!flow)break; } } if(!sum)dis[x]=-1; return sum; } int main() { read(n),read(m);logg=log2(n); for(int i=1;i<n;i++) { read(u),read(v); add(u,v,i),add(v,u,i); } now=n; Dfs(1,0); S=now+m+1; T=S+1; last=now; for(int i=1;i<=n;i++)addedge(i,T,1); for(int i=1,Lt;i<=m;i++) { read(v),read(u); now++; addedge(S,now,1); Lt=lca(v,u); link(v,u,Lt); } while(bfs(S,T))ans+=dfs(S,T,INF); printf("%d\n",ans); DFS_Put(S); for(int i=1;i<=m;i++) if(!flag[i+last])st[++top]=i; printf("%d ",top); while(top)printf("%d ",st[top--]); for(int i=1;i<=n;i++) if(flag[i])st[++top]=pos[i]; printf("\n"); printf("%d ",top); while(top)printf("%d ",st[top--]); return 0; }
时间: 2024-10-13 06:33:17