BZOJ 4543 POI2014 hotel

本文介绍了一个BZOJ问题的O(n)解法,通过深度优先搜索(DFS)预处理节点深度及最远节点,使用动态规划(dp)进行路径计数,实现了高效的求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem

BZOJ

Solution

我只会O(n^2)的解法。。
O(n)的思路是从neither_nor那里Orz来的(逃)
思路很神很神

可能以后再补一补?

Code

#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=100010;
struct data{int v,nxt;}edge[maxn<<1];
int n,p,head[maxn],deep[maxn],mx[maxn];
ll ans,tmp[maxn*5];
ll *ptr=tmp+1,*f[maxn],*g[maxn];
inline void insert(int u,int v)
{
    edge[++p]=(data){v,head[u]};head[u]=p;
    edge[++p]=(data){u,head[v]};head[v]=p;
}
void dfs(int x,int pre)
{
    mx[x]=x;
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v!=pre)
      {
        deep[edge[i].v]=deep[x]+1;
        dfs(edge[i].v,x);
        if(deep[mx[x]]<deep[mx[edge[i].v]]) mx[x]=mx[edge[i].v];
      }
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v!=pre&&(mx[edge[i].v]!=mx[x]||x==1))
      {
        int v=mx[edge[i].v];
        ptr+=deep[v]-deep[x]+1;
        f[v]=ptr;g[v]=(ptr+=1);
        ptr+=(deep[v]-deep[x])<<1|1;
      }
}
void dp(int x,int pre)
{
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v!=pre)
      {
        dp(edge[i].v,x);
        if(mx[x]==mx[edge[i].v]) f[x]=f[edge[i].v]-1,g[x]=g[edge[i].v]+1;
      }
    ans+=g[x][0];f[x][0]=1;
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v!=pre&&mx[x]!=mx[edge[i].v])
      {
        int lim=deep[mx[edge[i].v]]-deep[x];
        for(int j=0;j<=lim;j++)
          ans+=f[x][j-1]*g[edge[i].v][j]+g[x][j+1]*f[edge[i].v][j];
        for(int j=0;j<=lim;j++)
        {
            g[x][j-1]+=g[edge[i].v][j];
            g[x][j+1]+=f[x][j+1]*f[edge[i].v][j];
            f[x][j+1]+=f[edge[i].v][j];
        }
      }
}
void input()
{
    scanf("%d",&n);
    for(int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        insert(x,y);
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    input();
    deep[1]=1;
    dfs(1,0);
    dp(1,0);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值