【HDU5452】Minimum Cut

转送门
题目大意:已知一个 n 节点m条边的无向图以及它所对应的一棵生成树,要求只能删除一条树边和若干条非树边,求最少要删除多少条边。
注意:必须删除一条树边且只能删除一条树边(蒟蒻一开始理解成了至少删除一条树边QAQ)

既然必须删除一条树边,那么就枚举这一条树边吧o(>﹏<)o
删去这条树边之后,生成树就被分成了两个部分,我们只需要将连接这两个部分的边全部删除即可。

那么问题来了,怎样快速滴算出一条树边所对应的非树边呢(⊙o⊙)?
考虑一条非树边所影响的树边。这条边 e(u,v) ,必然是 u v之间的树边所对应的非树边。(理解:当这些树边被删除后, u v必定分属于两个不同的部分)
我第一次交的是用树剖直接在边的 cnt 上加 1 ,一交1860ms。然后我又原封不动滴交了一遍就T了/(ㄒoㄒ)/~~
之后写的是在 e(u,v) lca 上直接操作, 920ms ,无压力A掉。

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 100005
#define MAXM 830395
using namespace std;

int pos, adj[MAXN], n, ans[MAXN], a[MAXN];
struct node
{
    int v, next;
}edge[MAXN<<1], e[MAXM];

void add(int a,int b)
{
    edge[pos].v=b, edge[pos].next=adj[a];
    adj[a]=pos;++pos;
}

int road[MAXN<<1], c, cnt[MAXN];
void dfs(int u,int fa)
{
    road[++c]=u;
    for(int p=adj[u], v;~p;p=edge[p].next)
        if((v=edge[p].v)!=fa)
            dfs(v,u);
    road[++c]=-u;
}

int head[MAXN], pos2;
void Add(int a,int b)
{
    ++pos2;
    e[pos2].v=b, e[pos2].next=head[a];
    head[a]=pos2;
}

bool flag[MAXN];
int prime[MAXN], cntp, mo[MAXN];
void init()
{
    mo[1]=1;
    for(int i=2;i<=100000;++i)
    {
        if(!flag[i]){prime[++cntp]=i;mo[i]=-1;}
        for(int j=1;j<=cntp&&i*prime[j]<=100000;++j)
        {
            flag[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mo[i*prime[j]]=0;
                break;
            }
            mo[i*prime[j]]=-mo[i];
        }
    }
    memset(head,-1,sizeof head);
    for(int i=1;i<=100000;++i)
        if(mo[i])
            for(int j=i;j<=100000;j+=i)
                Add(j,i);
}

int main()
{
    init();
    int cas=0, u, v;
    while(~scanf("%d",&n))
    {
        memset(cnt,0,sizeof cnt), memset(adj,-1,sizeof adj);
        c=pos=0;
        for(int i=1;i<n;++i)
        {
            scanf("%d%d",&u,&v);
            add(u,v), add(v,u);
        }
        for(int i=1;i<=n;++i)scanf("%d",&a[i]);
        dfs(1,0);
        printf("Case #%d:",++cas);
        for(int i=1;i<=n;++i)ans[i]=a[i]==1;
        for(int i=1, t;i<=c;++i)
        {
            t=road[i];
            if(t>0)
            {
                for(int p=head[a[t]], b;~p;p=e[p].next)
                {
                    b=e[p].v;
                    ++cnt[b];
                    ans[t]-=mo[b]*cnt[b];
                }
            }
            else
            {
                t=-t;
                for(int p=head[a[t]], b;~p;p=e[p].next)
                {
                    b=e[p].v;
                    ans[t]+=mo[b]*cnt[b];
                }
            }
        }
        for(int i=1;i<=n;++i)printf(" %d",ans[i]);
        puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值