洛谷 P4149 [IOI2011]Race 点分治+dp

洛谷 P4149 [IOI2011]Race

求一棵树上路径长度刚好等于k的两点间的最小边数.如果不存在输出-1.
点分治不一定要容斥!!!!重要的事情要说三遍!
显然这题是点分治.
我们考虑 dp d p 搞,用 sum s u m 数组处理路径长度为 dis d i s 的两点间的最小边数,如果不存在为 inf i n f .
ans=min(ans,sum[k-dis[u]]+d[u])进行转移.
每一次 solve s o l v e 某个点之后暴力把没有处理过的点的 sum[dis] s u m [ d i s ] 都直接赋值为 inf i n f .
最后注意每一次把 sum[0] s u m [ 0 ] 赋值为 0 0 即可.

#include //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
#define int ll
const int yuzu=2e6,mulu=1e7,inf=0x3f3f3f3f;
typedef int fuko[yuzu|10];
struct edge{int to,cost;};
vector lj[yuzu|10];
fuko sz,mxs,vis,d,dis;
int n,k,rt,nsz,xiao,ans=inf,sum[mulu|10];
/*建议把数组大小开大10倍.*/
void grt(int u,int fa){
sz[u]=1,mxs[u]=0;
for (edge i:lj[u]){
  int v=i.to,c=i.cost;
  if (!vis[v]&&v^fa){
    grt(v,u),sz[u]+=sz[v];
    mxs[u]=max(mxs[u],sz[v]);
    }
  }
mxs[u]=max(mxs[u],nsz-mxs[u]);
if (mxs[u]void gdp(int u,int fa,int dep,int dist){
d[u]=dep,dis[u]=dist;
if (dis[u]>k) return;//不加这个剪枝会爆栈RE.
ans=min(ans,sum[k-dis[u]]+d[u]);
for (edge i:lj[u]){
  int v=i.to,c=i.cost;
  if (v^fa&&!vis[v]) gdp(v,u,dep+1,dist+c);
  }
}

void dfs(int u,int fa,bool nico){
sum[dis[u]]=nico?min(sum[dis[u]],d[u]):inf;
for (edge i:lj[u]){
  int v=i.to;
  if (!vis[v]&&v^fa) dfs(v,u,nico);
  }
}

#define init(x) rt=0,nsz=x,xiao=inf
void solve(int u){
vis[u]=1,sum[0]=0;
for (edge i:lj[u]){
  int v=i.to;
  if (!vis[v]){
    gdp(v,0,1,i.cost);//get的时候不要忘记从u到v的这条边.
    dfs(v,0,true);
    }
  }
for (edge i:lj[u]){
  int v=i.to;
  if (!vis[v]) dfs(v,0,false);
  }
for (edge i:lj[u]){
  int v=i.to,c=i.cost;
  if (!vis[v]){
    init(sz[v]),grt(v,0);
    solve(rt);
    }
  }
}

main(){
int i,j;n=read(),k=read();
for (i=1;iint u=read()+1,v=read()+1,c=read();//注意点从0开始编号.
  lj[u].push_back(edge{v,c});
  lj[v].push_back(edge{u,c});
  }
fill(sum+1,sum+mulu+1,inf);
init(n),grt(1,0),solve(rt);
write(ans^inf?ans:-1);
}
/*
4 3
0 1 1
1 2 2
1 3 4
*/

你可能感兴趣的:(点分治,dp)