BZOJ4379 : [POI2015]Modernizacja autostrady

两遍树形DP求出每个点开始往上往下走的前3长路以及每个点上下部分的直径。

枚举每条边断开,设两边直径分别为$A,B$,则:

对于第一问,连接两边直径的中点可得直径为$\max(A,B,\lfloor\frac{A+1}{2}\rfloor+\lfloor\frac{B+1}{2}\rfloor+1)$的新树。

对于第二问,连接两边直径的端点可得直径为$A+B+1$的新树。

时间复杂度$O(n)$。

#include<cstdio>
#define N 500010
int n,i,j,x,y,g[N],v[N<<1],nxt[N<<1],del[N<<1],ed,fa[N];
int d[N][3],f[N],w[N][2],u[N],h[N],ans1=N,ans2,ch1,ch2,id,dis[N],pre[N],q[N];
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline void up(int&a,int b){if(a<b)a=b;}
inline int max(int a,int b){return a>b?a:b;}
void caldown(int x){
  for(int i=g[x];i;i=nxt[i])if(v[i]!=fa[x]){
    int y=v[i];
    fa[v[i]]=x;
    caldown(v[i]);
    int t=d[y][0]+1;
    if(t>d[x][0])d[x][2]=d[x][1],d[x][1]=d[x][0],d[x][0]=t;
    else if(t>d[x][1])d[x][2]=d[x][1],d[x][1]=t;
    else if(t>d[x][2])d[x][2]=t;
    up(f[x],t=f[y]);
    if(t>w[x][0])w[x][1]=w[x][0],w[x][0]=t;else if(t>w[x][1])w[x][1]=t;
  }
  up(f[x],d[x][0]+d[x][1]);
}
void calup(int x){
  if(x!=1){
    int y=fa[x];
    u[x]=u[y];
    up(u[x],d[y][0]==d[x][0]+1?d[y][1]:d[y][0]);
    u[x]++;
    h[x]=h[y];
    up(h[x],w[y][0]==f[x]?w[y][1]:w[y][0]);
    int a,b,c=u[y];
    if(d[y][0]==d[x][0]+1)a=d[y][1],b=d[y][2];
    else a=d[y][0],b=d[y][1]==d[x][0]+1?d[y][2]:d[y][1];
    up(h[x],a+b),up(h[x],a+c),up(h[x],b+c);
    int t=max(max(h[x],f[x]),(h[x]+1)/2+(f[x]+1)/2+1);
    if(t<ans1)ans1=t,ch1=x;
    t=h[x]+f[x]+1;
    if(t>ans2)ans2=t,ch2=x;
    up(h[x],a+1),up(h[x],b+1),up(h[x],c+1);
  }
  for(int i=g[x];i;i=nxt[i])if(v[i]!=fa[x])calup(v[i]);
}
void dfs(int x,int y){
  pre[x]=y;
  if(dis[x]>dis[id])id=x;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&!del[i])dis[v[i]]=dis[x]+1,dfs(v[i],x);
}
int main(){
  read(n);
  for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
  caldown(1),calup(1);
  printf("%d %d %d",ans1,ch1,fa[ch1]);
  for(i=g[ch1];i;i=nxt[i])if(v[i]==fa[ch1])del[i]=1;
  for(i=g[fa[ch1]];i;i=nxt[i])if(v[i]==ch1)del[i]=1;
  dfs(id=ch1,dis[ch1]=0);
  dfs(id,dis[id]=0);
  for(i=1;id;i++,id=pre[id])q[i]=id;
  printf(" %d",q[i>>1]);
  dfs(id=fa[ch1],dis[fa[ch1]]=0);
  dfs(id,dis[id]=0);
  for(i=1;id;i++,id=pre[id])q[i]=id;
  printf(" %d\n",q[i>>1]);
  for(i=g[ch1];i;i=nxt[i])if(v[i]==fa[ch1])del[i]=0;
  for(i=g[fa[ch1]];i;i=nxt[i])if(v[i]==ch1)del[i]=0;
  printf("%d %d %d",ans2,ch2,fa[ch2]);
  for(i=g[ch2];i;i=nxt[i])if(v[i]==fa[ch2])del[i]=1;
  for(i=g[fa[ch2]];i;i=nxt[i])if(v[i]==ch2)del[i]=1;
  dfs(id=ch2,dis[ch2]=0);
  printf(" %d",id);
  dfs(id=fa[ch2],dis[fa[ch2]]=0);
  printf(" %d",id);
  return 0;
}

  

时间: 2024-10-14 10:05:42

BZOJ4379 : [POI2015]Modernizacja autostrady的相关文章

[Poi2015]

[POI2015]?asuchy 一看以为是sb题 简单来说就是每个人获得热量要尽量多 不能找别人 首先这道题好像我自己找不到NIE的情况 很容易想到一个优化 如果一个数/2>另一个数 那么一定选这个数 然后我想着其他的话就随便分配一个 然后会得出下一个 其实这样做是错的 因为你选完之后不知道下一个会不会是来降低我当前选的那一个的热量使得我当前的原来最优变成不是最优 然后这样子 怎么办呢??? 废话 膜题解 膜拜Claris 我们既然不知道下一个会不会来降低热量 不妨把每个食物的状态都定下来 让

bzoj 4385: [POI2015]Wilcze do?y

4385: [POI2015]Wilcze do?y Description 给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0.请找到最长的一段连续区间,使得该区间内所有数字之和不超过p. Input 第一行包含三个整数n,p,d(1<=d<=n<=2000000,0<=p<=10^16).第二行包含n个正整数,依次表示序列中每个数w[i](1<=w[i]<=10^9). Output 包含一行一个正整数,即修改后能

BZOJ 3747: [POI2015]Kinoman( 线段树 )

线段树... 我们可以枚举左端点 , 然后用线段树找出所有右端点中的最大值 . ----------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( i

poi2015 bzoj4377-4386训练

就按时间顺序写吧 完成度:8/10 3.30 bzoj4385 首先一定是删去连续d个数,然后枚举终点,起点显然有单调性,用单调队列乱搞搞就可以啦 bzoj4378 首先才结论:可行当且仅当把所有大于s的数全变成s然后看所有的数的和大于等于c*s,然后两个树状数组分别维护<=s的和及个数即可,注意需要离散化 3.31 bzoj4377 设一段的起点处的数为x,则m个限制条件就可以转化为x在若干个区间(或两个区间的并)里面,然后把这些区间交起来就得到了x的范围,算出个数然后减去最后m-1个数(没有

【BZOJ 3747】 3747: [POI2015]Kinoman (线段树)

3747: [POI2015]Kinoman Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 830  Solved: 338 Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,-,r天内所有的电影.如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值

【BZOJ4378】[POI2015]Logistyka 树状数组

[BZOJ4378][POI2015]Logistyka Description 维护一个长度为n的序列,一开始都是0,支持以下两种操作:1.U k a 将序列中第k个数修改为a.2.Z c s 在这个序列上,每次选出c个正数,并将它们都减去1,询问能否进行s次操作.每次询问独立,即每次询问不会对序列进行修改. Input 第一行包含两个正整数n,m(1<=n,m<=1000000),分别表示序列长度和操作次数.接下来m行为m个操作,其中1<=k,c<=n,0<=a<=

BZOJ 4384: [POI2015]Trzy wie?e

4384: [POI2015]Trzy wie?e Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 217  Solved: 61[Submit][Status][Discuss] Description 给定一个长度为n的仅包含'B'.'C'.'S'三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符,要么有多种字符,但是没有任意两种字符出现次数相同. Input 第一行包含一个正整数n(1<=n<=1000000),表示字符

【BZOJ3747】[POI2015]Kinoman 线段树

[BZOJ3747][POI2015]Kinoman Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影.如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值.所以你希望最大化观看且仅观看过一次的电影的好看值的总和. Input 第一行两个整数n,m(1<=m<=n&

洛谷P3588 - [POI2015]Pustynia

Portal Description 给定一个长度为\(n(n\leq10^5)\)的正整数序列\(\{a_n\}\),每个数都在\([1,10^9]\)范围内,告诉你其中\(s\)个数,并给出\(m(m\leq2\times10^5)\)条信息.每条信息包含三个数\(L,R,k(Σk\leq 3\times10^5)\)以及\(k\)个正整数\(\{x_k\}\),表示\(a_L..a_R\)中,任意一个\(x\)均比剩下的\(R-L+1-k\)个数大(严格大于,即没有等号).请任意构造出一组