[bzoj 3611] [heoi 2014] 大工程

题目在这里
正解是虚树,然而我并不会写。。。。

所以我写了点分治。

对于一棵子树,暴力找出重心,然后计算跨越重心的部分对答案的贡献。
具体方法如下:对于重心的每一棵子树,算出每个询问中在这棵子树中的每个节点到重心距离的最大值,最小值,距离和以及个数,然后dfs的时候更新一下。
(速度巨慢,bzoj上倒数)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <deque>
#include <queue>
#include <map>
#include <set>
#include <ctime>
using namespace std;

typedef long long LL; 
const int MAXN=1000005,MAXQ=50005,INF=(1<<29);
int n,m,tot,fst[MAXN],pre[MAXN*2],to[MAXN*2],dis[MAXN],son[MAXN],size[MAXN],maxsize[MAXN];

struct Node
{
    int _max,_min,cnt; LL sum; 
    void init() { _max=-INF,_min=INF,sum=0,cnt=0; }
    void update(int x) { _max=max(_max,dis[x]),_min=min(_min,dis[x]),sum+=LL(dis[x]),cnt++; }
}res[MAXQ],ans[MAXQ]; 

void modify(int x,int q)
{
    ans[q]._min=min(ans[q]._min,dis[x]+res[q]._min);
    ans[q]._max=max(ans[q]._max,dis[x]+res[q]._max);
    ans[q].sum+=LL(res[q].cnt)*LL(dis[x])+res[q].sum;
}
vector<int> Ask[MAXN]; bool vis[MAXN];

int Get()
{
    char ch; int v=0; bool f=false;
    while (!isdigit(ch=getchar())) if (ch=='-') f=true; v=ch-48;
    while (isdigit(ch=getchar())) v=v*10+ch-48;
    if (f) return -v;else return v; 
}

void add(int x,int y) 
{
    pre[++tot]=fst[x],fst[x]=tot,to[tot]=y;
    pre[++tot]=fst[y],fst[y]=tot,to[tot]=x; 
}

void dfs_root(int x,int fa)
{   
    son[++tot]=x; size[x]=1,maxsize[x]=0; 
    for (int i=fst[x];i;i=pre[i])
      {
        int y=to[i];
        if (!vis[y] && fa!=y) dfs_root(y,x),size[x]+=size[y],maxsize[x]=max(size[y],maxsize[x]); 
      }
}

void dfs_init(int x,int fa)
{       
    son[++tot]=x; 
    for (int i=0;i<Ask[x].size();i++) modify(x,Ask[x][i]);    
    for (int i=fst[x];i;i=pre[i])  
      {
        int y=to[i];
        if (!vis[y] && y!=fa) dis[y]=dis[x]+1,dfs_init(y,x);     
      }
}

void work(int x)
{   
    tot=0,dis[x]=0; 
    for (int i=fst[x];i;i=pre[i])
      {
        int y=to[i],st=tot; dis[y]=1;
        if (!vis[y]) 
          {
            dfs_init(y,x); 
            for (int j=st+1;j<=tot;j++) 
              for (int k=0;k<Ask[son[j]].size();k++) res[Ask[son[j]][k]].update(son[j]); 
          }
      }
    for (int i=0;i<Ask[x].size();i++) modify(x,Ask[x][i]);
    for (int i=1;i<=tot;i++) 
      { 
        dis[son[i]]=0;
        for (int j=0;j<Ask[son[i]].size();j++) res[Ask[son[i]][j]].init(); 
      }
} 

void dfs(int x)
{   
    tot=0; int root;
    dfs_root(x,-1); int ms=MAXN;
    for (int i=1;i<=tot;i++) 
      {
        int tmp=max(maxsize[son[i]],tot-size[son[i]]); 
        if (tmp<ms) ms=tmp,root=son[i];
      } 
    work(root); vis[root]=true; 
    for (int i=fst[root];i;i=pre[i]) 
      { 
        int y=to[i]; 
        if (!vis[y]) dfs(y);
      }
}

void Put(int x)
{
    char ch[21]; int tot=0; if (x==0) { putchar('0'); return; }
    while (x) ch[++tot]=x%10,x/=10; 
    for (;tot;tot--) putchar(ch[tot]+48); 
}

void Putll(LL x)
{ 
    char ch[21]; int tot=0; if (x==0) { putchar('0'); return; }
    while (x) ch[++tot]=x%LL(10),x/=LL(10); 
    for (;tot;tot--) putchar(ch[tot]+48); 
}

int main()
{
    //freopen("project.in","r",stdin);
    //freopen("project.out","w",stdout);
    n=Get(); int u,v;
    for (int i=1;i<=n-1;i++) u=Get(),v=Get(),add(u,v);
    m=Get();
    for (int i=1;i<=m;i++) 
      {
        tot=Get(); for (int j=1;j<=tot;j++) u=Get(),Ask[u].push_back(i); 
        res[i].init(),ans[i].init();  
      }

    dfs(1);
    for (int i=1;i<=m;i++) Putll(ans[i].sum),putchar(32),Put(ans[i]._min),
    putchar(32),Put(ans[i]._max),puts("");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值