转送门
题目大意:已知一个
n
节点
注意:必须删除一条树边且只能删除一条树边(蒟蒻一开始理解成了至少删除一条树边QAQ)
既然必须删除一条树边,那么就枚举这一条树边吧o(>﹏<)o
删去这条树边之后,生成树就被分成了两个部分,我们只需要将连接这两个部分的边全部删除即可。
那么问题来了,怎样快速滴算出一条树边所对应的非树边呢(⊙o⊙)?
考虑一条非树边所影响的树边。这条边
e(u,v)
,必然是
u
到
我第一次交的是用树剖直接在边的
cnt
上加
1
,一交
之后写的是在
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;
}