Description
在很久以前有一片土地被称为 Dudeland。Dudeland 包含 n 个城镇, 它们用 n − 1
条双向道路连接起来。这些城镇通过道路可以两两互达。这 里有 m 个修道院坐落在 m 个不同的城镇。每个修道院有一个教徒。
在一年之始,每个教徒会选择离他最远的一个修道院。如果有多个, 他会把所有的都列入清单。在 “BigLebowskiday”
里,每个教徒会随机选 择一个清单里的城镇开始走去。 Walter 讨厌教徒。他想尽可能的通过阻止他们的行程来让尽可能多的
人不开心。他计划摧毁一个没有修道院的城镇。一个教徒如果在他的清单 里没有任何一个城镇能去,他就会不开心。 你需要求出 Walter
最多能让几个教徒不开心。除此之外,你还要计算 他有多少种方法。
Input
第一行包含两个整数 n,m,满足 3 ≤ n ≤ 10^5, 2 ≤ m<n。 接下来一行,有 m
个互不相同的整数,他们代表了有修道院的城镇的 编号。 接下来 n − 1 行,每行三个整数 ai,bi,ci,表示 ai,bi
之间有一条边权 为 ci 的边。(1 ≤ ai,bi ≤ n,ai = bi,ci ≤ 1000)
Output
输出两个数:最多能让几个教徒不开心,以及有多少种方式达到这种效果。
Sample Input
8 5
7 2 5 4 8
1 2 1
2 3 2
1 4 1
4 5 2
1 6 1
6 7 8
6 8 10
Sample Output
5 1
前言
bzoj的数据真是辣鸡怎么写都能过…
写错了一堆重要地方都过了什么辣鸡数据…
哦CF的好像也有点问题…
BTW Claris的代码似乎被我拍出了一个错…
放一下数据
INPUT:
15 12
1 9 13 12 11 10 5 4 7 14 3 6
2 1 2
3 2 1
4 1 2
5 4 1
6 3 1
7 1 1
8 5 1
9 3 1
10 5 1
11 1 1
12 4 1
13 3 1
14 1 1
15 5 1
OUTPUT:
8 1
题解
先考虑辣鸡做法
对每个修道院找到所有距离他最长的修道院
能割断他们的联系的显然是这些修道院的LCA到当前修道院的链上所有点
如果子树内有并且子树外也有显然这个修道院不能做贡献
树上差分一下最后扫一遍就行了
考虑如何优化这个做法
对于每个点,我们维护一些值
他到子树内某个点的最大值与次大值,同时强制让这两个点不能在同一棵子树
维护down表示这两个点的编号
维护count表示最大值和次大值出现了多少次
考虑如何找LCA的过程
如果这个点的不同子树最大值出现了多于一次,那么他们被割断的LCA至少是当前点
否则可以直接继承最大值所在的子树的割断点编号
对于子树外的,每次往下的时候把贡献加上
处理子树外贡献的时候维护最长链以及割断编号
这个基础上用树上差分扫一遍就ok了
我怎么写的这么长啊
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
const int inf=(1<<31-1);
struct node{int x,y,c,next;}a[210000];int len,last[110000];
void ins(int x,int y,int c){len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
struct pt{int mx[3],ct,ct1,down,down1;}w[110000];
int fa[110000][25],dep[110000],bin[25];
int in[110000],ot[110000],dfn;
void pre_tree_node(int x)
{
in[x]=++dfn;
for(int i=1;bin[i]<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa[x][0])
{
fa[y][0]=x;dep[y]=dep[x]+1;
pre_tree_node(y);
}
}
ot[x]=dfn;
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;i--)if(bin[i]<=dep[x]&&dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=20;i>=0;i--)if(bin[i]<=dep[x]&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void updata(int u,int v,int c)
{
if(w[v].mx[0]==-inf)return ;
int dis=w[v].mx[0]+c;
if(w[u].mx[0]<dis)
{
swap(w[u].mx[2],w[u].mx[1]);swap(w[u].mx[1],w[u].mx[0]);swap(w[u].down1,w[u].down);swap(w[u].ct,w[u].ct1);
w[u].mx[0]=dis;w[u].ct=1;if(w[v].ct>1)w[u].down=v;else w[u].down=w[v].down;
}
else if(w[u].mx[0]==dis)
{
swap(w[u].mx[2],w[u].mx[1]);swap(w[u].mx[1],w[u].mx[0]);swap(w[u].down1,w[u].down);w[u].ct1=w[u].ct;
w[u].mx[0]=dis;w[u].ct++;w[u].ct1++;if(w[v].ct>1)w[u].down=v;else w[u].down=w[v].down;
}
else if(w[u].mx[1]<dis)
{
swap(w[u].mx[1],w[u].mx[2]),w[u].mx[1]=dis,w[u].ct1=1;
if(w[v].ct>1)w[u].down1=v;else w[u].down1=w[v].down;
}
else if(w[u].mx[1]==dis)
{
swap(w[u].mx[1],w[u].mx[2]);w[u].mx[1]=dis;
if(w[v].ct>1)w[u].down1=v;else w[u].down1=w[v].down;
w[u].ct1++;
}
else if(w[u].mx[2]<dis)w[u].mx[2]=dis;
}
bool v[110000];
void dp1(int x)
{
w[x].mx[0]=w[x].mx[1]=w[x].mx[2]=-inf;
if(v[x])w[x].mx[0]=0,w[x].down=x;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa[x][0])dp1(y),updata(x,y,a[k].c);
}
}
int sum[110000];
void dp2(int x,int cal,int pos)//上面最长链权值 位置
{
if(w[x].mx[0]>cal&&v[x])
{
if(w[x].ct<=1)
{
int LA=lca(x,w[x].down);
sum[w[x].down]++;sum[x]++;sum[LA]--;sum[fa[LA][0]]--;
// printf("%d %d\n",x,w[x].down);
}
}
else if(w[x].mx[0]<cal&&v[x])
{
int LA=lca(x,pos);
sum[pos]++;sum[x]++;sum[LA]--;sum[fa[LA][0]]--;
// printf("%d %d\n",x,pos);
}
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa[x][0])
{
int dis=w[y].mx[0]+a[k].c,pa,sss;
if(dis==w[x].mx[0])
{
pa=w[x].mx[1]+a[k].c;
if(w[x].ct1>2&&w[x].mx[1]==w[x].mx[0])sss=x;
else if(w[x].ct1==2&&w[x].mx[1]==w[x].mx[0])
{
if(in[w[x].down1]>=in[y]&&in[w[x].down1]<=ot[y])sss=w[x].down;
else sss=w[x].down1;
}
else if(w[x].ct1>=2&&w[x].mx[1]!=w[x].mx[0])sss=x;
else
{
if(in[w[x].down1]>=in[y]&&in[w[x].down1]<=ot[y])sss=w[x].down;
else sss=w[x].down1;
}
}
else
{
pa=w[x].mx[0]+a[k].c;
sss=w[x].ct>1?x:w[x].down;
}
if(pa==cal+a[k].c)dp2(y,cal+a[k].c,x);
else if(pa<cal+a[k].c)dp2(y,cal+a[k].c,pos);
else
{
if(dis==w[x].mx[0])dp2(y,pa,sss);
else dp2(y,pa,sss);
}
}
}
}
void merge(int x)
{
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa[x][0])merge(y),sum[x]+=sum[y];
}
}
int n,m;
int main()
{
bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]<<1;
n=read();m=read();
memset(v,false,sizeof(v));
for(int i=1;i<=m;i++)v[read()]=true;
for(int i=1;i<n;i++)
{
int x=read(),y=read(),c=read();
ins(x,y,c);ins(y,x,c);
}
pre_tree_node(1);
dp1(1);
dp2(1,v[1]?0:-inf,0);
// for(int i=1;i<=n;i++)printf("ID %d: %d %d %d %d %d %d %d\n",i,w[i].mx[0],w[i].mx[1],w[i].mx[2],w[i].down,w[i].down1,w[i].ct,w[i].ct1);
merge(1);
int s=0,ans=0;
for(int i=1;i<=n;i++)if(!v[i])s=max(s,sum[i]);
for(int i=1;i<=n;i++)if(sum[i]==s&&!v[i])ans++;
printf("%d %d\n",s,ans);
return 0;
}