题目描述
Tree is a connected acyclic graph. Suppose you are given a tree consisting of nn vertices. The vertex of this tree is called centroid if the size of each connected component that appears if this vertex is removed from the tree doesn't exceed
.
You are given a tree of size nn and can perform no more than one edge replacement. Edge replacement is the operation of removing one edge from the tree (without deleting incident vertices) and inserting one new edge (without adding new vertices) in such a way that the graph remains a tree. For each vertex you have to determine if it's possible to make it centroid by performing no more than one edge replacement.
输入格式
The first line of the input contains an integer nn ( 2<=n<=4000002<=n<=400000 ) — the number of vertices in the tree. Each of the next n-1n−1 lines contains a pair of vertex indices u_{i}ui and v_{i}vi ( 1<=u_{i},v_{i}<=n1<=ui,vi<=n ) — endpoints of the corresponding edge.
输出格式
Print nn integers. The ii -th of them should be equal to 11 if the ii -th vertex can be made centroid by replacing no more than one edge, and should be equal to 00 otherwise.
题意翻译
给定一颗树,你有一次将树改造的机会,改造的意思是删去一条边,再加入一条边,保证改造后还是一棵树。
请问有多少点可以通过改造,成为这颗树的重心?(如果以某个点为根,每个子树的大小都不大于\dfrac{n}{2}2n,则称某个点为重心)
输入输出样例
输入 #1复制
3 1 2 2 3
输出 #1复制
1 1 1
输入 #2复制
5 1 2 1 3 1 4 1 5
输出 #2复制
1 0 0 0 0
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=4e5+5;
int head[maxn];
int n;
struct node{
int next,to;
}edge[maxn<<1];
int cnt=0;
void add_edge(int from,int to)
{
edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt++;
}
int f[maxn][2],ff[maxn],s[maxn],pos[maxn],sm[maxn];
void dfs1(int root,int pre)
{
s[root]=1;
for(int i=head[root];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to!=pre)
{
dfs1(to,root);
int v;
s[root]+=s[to];
if(s[to]>s[sm[root]]) sm[root]=to;
if(s[to]<=n/2) v=s[to];
else v=f[to][0];
if(f[root][0]<v)
{
f[root][1]=f[root][0];f[root][0]=v;pos[root]=to;
}
else if(f[root][1]<v) f[root][1]=v;
}
}
}
int ans[maxn];
void dfs2(int root,int pre)
{
ans[root]=1;
if(s[sm[root]]>n/2) ans[root]=(s[sm[root]]-f[sm[root]][0]<=n/2);
else if(n-s[root]>n/2) ans[root]=(n-s[root]-ff[root]<=n/2);
for(int i=head[root];i!=-1;i=edge[i].next)
{
int to=edge[i].to;int v;
if(to!=pre)
{
if(n-s[root]>n/2) v=ff[root];
else v=n-s[root];
ff[to]=max(ff[to],v);
if(to==pos[root]) ff[to]=max(ff[to],f[root][1]);
else ff[to]=max(ff[to],f[root][0]);
dfs2(to,root);
}
}
}
int main()
{
cin>>n;
memset(head,-1,sizeof(head));
for(int i=1;i<n;i++)
{
int from,to;cin>>from>>to;
add_edge(from,to);
add_edge(to,from);
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
}
题目描述
粉兔的妹子跑了!
于是,粉兔要去找她跑掉的妹子!
粉兔的妹子在福州某中上学 。
福州某中共有 N 个路口 ,编号从 1 到 N。路口之间由 N−1 条双向通行的道路连接,并且任意两个路口之间都是连通的。所有只有一条道路与之连接的路口都是福州某中的出入口。一些路口旁有教学楼。粉兔的妹子每天会从某个入口进去,到某栋教学楼上课,然后再从某个出口离开福州某中 。
由于妹子热爱锻炼身♂体, 所以他总是想使自己一天中走的总路程尽量长。当然,妹子不喜欢走重复的路,所以每天他都不会经过同一条路两次。也就是说,妹子会选择一对出入口使得从到教学楼、出口的距离和最大,并且两条路径不会经过同一条道路。可以证明,在不重复经过同一条道路的前提下,连接两个路口的路径是唯一的,从而它们间的距离也是唯一的。
粉兔通过某种手段控制了福州某中所有教学楼里的监摄像头,并掌握了每天妹子上课的教学楼。现在她想知道,妹子每天分别走了多长的路?
注意两个相同路口的距离为 0。
输入
第一行两个整数 N 和 M,分别表示福州某中的路口个数和总天数。
接下来 N−1 行,每行三个整数,Si,Ti和 Li,代表一条连接路口 Si 和 Ti、长度为 Li的道路 。
接下来 M 行,每一个整数 Ai,代表妹子第 i 天上课的教学楼旁路口编号。
输入数据保证合法。
输出
输出 M 行,分别代表妹子每一天走的路程之和。
样例输入 Copy
8 3
1 2 2
2 3 4
3 4 1
4 5 7
4 6 3
2 7 5
1 8 6
3
6
7
样例输出 Copy
20
16
17
const int maxn=1e6+7;
vector<PLL>g[maxn];
ll dp[maxn][2],ans[maxn],n,m;
void dfs1(int u,int fa){
dp[u][0]=dp[u][1]=0;
for(auto t:g[u]){
ll nex=t.first,w=t.second;
if(nex==fa) continue;
dfs1(nex,u);
if(dp[u][0]<dp[nex][0]+w){
dp[u][1]=dp[u][0];
dp[u][0]=dp[nex][0]+w;
}
else if(dp[u][1]<dp[nex][0]+w){
dp[u][1]=dp[nex][0]+w;
}
}
}
void dfs2(int u,int fa){
for(auto t:g[u]){
ll v=t.first,w=t.second;
if(v==fa) continue;
if(dp[v][0]+w==dp[u][0]){
ll d=w+dp[u][1];
if(dp[v][0]<d){
dp[v][1]=dp[v][0];
dp[v][0]=d;
}
else if(dp[v][1]<d){
dp[v][1]=d;
}
}
else{
ll d=w+dp[u][0];
if(dp[v][0]<d){
dp[v][1]=dp[v][0];
dp[v][0]=d;
}
else if(dp[v][1]<d){
dp[v][1]=d;
}
}
ans[v]=dp[v][0]+dp[v][1];
dfs2(v,u);
}
}
int main(){
n=read,m=read;
rep(i,1,n-1){
ll u=read,v=read,w=read;
g[u].push_back({v,w});
g[v].push_back({u,w});
}
dfs1(1,-1);
ans[1]=dp[1][0]+dp[1][1];
dfs2(1,-1);
while(m--){
int x=read;
printf("%lld\n",ans[x]);
}
return 0;
}
题目描述
在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 11 到 nn。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:
Append(w, v)
:一个新的珠子 ww 和一个已经添加的珠子 vv 用红线连接起来。
Insert(w, u, v)
:一个新的珠子 ww 插入到用红线连起来的两个珠子 u, vu,v 之间。具体过程是删去 u, vu,v 之间红线,分别用蓝线连接 u, wu,w 和 w, vw,v。
每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。
给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。
你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。
输入格式
第一行一个正整数 nn,表示珠子的数量。珠子从 11 到 nn 编号。
接下来 n - 1n−1 行每行三个整数 a_i, b_i, c_iai,bi,ci。保证 1 \leq a_i < b_i \leq n1≤ai<bi≤n。1 \leq c_i \leq 100001≤ci≤10000。表示 a_iai 号珠子和 b_ibi 号珠子间连了长度为 c_ici 的线。
输出格式
输出一个整数,表示最大可能得分。
输入输出样例
输入 #1复制
5 1 2 10 1 3 40 1 4 15 1 5 20
输出 #1复制
60
输入 #2复制
10 4 10 2 1 2 21 1 3 13 6 7 1 7 9 5 2 4 3 2 5 8 1 6 55 6 8 34
输出 #2复制
140
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=2e5+5;
const int INF=1e9;
int head[maxn];
struct node{
int to,next,cost;
}edge[maxn<<1];
int f[maxn][2],g[maxn][2],k[maxn][2];
int mx1[maxn],mx2[maxn],ve[maxn],son1[maxn],son2[maxn];
int cnt=0;
void add_edge(int from,int to,int cost)
{
edge[cnt].to=to;
edge[cnt].next=head[from];
edge[cnt].cost=cost;
head[from]=cnt++;
}
void dfs1(int root ,int pre)
{
mx1[root]=mx2[root]=-INF;
for(int i=head[root];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to==pre) continue;
ve[to]=edge[i].cost;
dfs1(to,root);
f[root][0]+=max(f[to][0],f[to][1]+edge[i].cost);
int t=f[to][0]+edge[i].cost-max(f[to][0],f[to][1]+edge[i].cost);
if(t>mx1[root])
{
mx2[root]=mx1[root];son2[root]=son1[root];
son1[root]=to;mx1[root]=t;
}
else if(t>mx2[root]) mx2[root]=t,son2[root]=to;
}
f[root][1]=f[root][0]+mx1[root];
}
void dfs2(int root,int pre)
{
for(int i=head[root];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to==pre) continue;
if(son1[root]==to) {swap(son1[root],son2[root]),swap(mx1[root],mx2[root]);}
k[root][0]=g[root][0]-max(f[to][0],f[to][1]+edge[i].cost);k[root][1]=k[root][0]+mx1[root];
if(pre) {
k[root][1]=max(k[root][1],k[root][0]+k[pre][0]+ve[root]-max(k[pre][0],k[pre][1]+ve[root]));
}
g[to][0]=f[to][0]+max(k[root][0],k[root][1]+edge[i].cost);
if(mx1[root]<mx2[root]) {swap(son1[root],son2[root]),swap(mx1[root],mx2[root]);}
dfs2(to,root);
}
}
int main()
{
memset(head,-1,sizeof(head));
int n;cin>>n;
for(int i=1;i<n;i++)
{
int from,to,cost;
cin>>from>>to>>cost;
add_edge(from,to,cost);
add_edge(to,from,cost);
}
dfs1(1,0);
g[1][0]=f[1][0];
dfs2(1,0);
int ans=0;
for(int i=1;i<=n;i++)
{
ans=max(ans,g[i][0]);
}
cout<<ans<<endl;
}