hdu 4123
树的直径 + 单调队列 + 尺取
50000个点的树,每个点有一个人,每个人会跑到离自己初始点距离最远的点上,这个距离为distance[i]。给你500个查询,对于每个查询Q,找一段连续编号的人,比如[left,right],满足 max( distance[i] i∈[left,right] ) – min( distance[i] i∈[left,right] ) ≤ Q,并且使得length=right-left+1要最大,求这个最大的length
找距离最远点即求树的直径,两次bfs。后dfs求某点到树的最远距离。单调队列维护最大最小值
代码:
#include <bits/stdc++.h>
using namespace std;
vector<int>v[100000],val[100000];
long long dis[100000],dp[100000],len,pre[100000],vis[100000];
//树的直径两次bfs第一次任选一点走到距离这点最远,第二次从最远点,找到距离这点最远
//dfs处理从这点开始到这个点能到的每个点的距离
int n,m;
int bfs(int xx)
{
int far=xx;
for(int i=1;i<=n;i++)
dis[i]=-1;
dis[xx]=0;
pre[xx]=-1;
queue<int>q;
q.push(xx);
while(!q.empty())
{
int x = q.front();
q.pop();
for(int i=0;i<v[x].size();i++)
{
if(dis[v[x][i]]==-1)
{
dis[v[x][i]]=dis[x]+val[x][i];
q.push(v[x][i]);
pre[v[x][i]]=x;
if(dis[v[x][i]]>dis[far])
far=v[x][i];
}
}
}
return far;
}
void dfs(int x)
{
vis[x] = 1;
for(int i=0;i<v[x].size();i++)
{
if(!vis[v[x][i]])
{
dp[v[x][i]]=dp[x]+val[x][i];
dfs(v[x][i]);
}
}
}
void init()
{
int S = bfs(bfs(1));
len = dis[S];
for(int i=1;i<=n;i++)
vis[i]=0;
queue<int>q;
int x=S;
while(x!=-1)//一个while循环的到一条链上的最远距离,并记录这条链上的点
{
q.push(x);
dp[x] = max(dis[x],len-dis[x]);
vis[x]=1;
x=pre[x];
}
while(!q.empty())//从链上的每一个点出发到各个点的距离
{
dfs(q.front());
q.pop();
}
}
struct node{
int id;
long long val;
};
int main()
{
while(~scanf("%d%d",&n,&m)&&n+m)
{
for(int i=1;i<=n;i++)
v[i].clear(),val[i].clear();
int x,y,z;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
v[x].push_back(y);
v[y].push_back(x);
val[x].push_back(z);
val[y].push_back(z);
}
init();
while(m--)
{
int q;
scanf("%d",&q);
deque<node>mx;
deque<node>mn;
int idx=1;
int ans=0;
for(int i=1;i<=n;i++)
{
while(!mx.empty()&&mx.back().val<=dp[i])
{
mx.pop_back();
}
mx.push_back((node){i,dp[i]});
while(!mn.empty()&&mn.back().val>=dp[i])
{
mn.pop_back();
}
mn.push_back((node){i,dp[i]});
while(!mx.empty()&&!mn.empty())
{
if(mx.front().val-mn.front().val>q)
{
idx++;
}
else
break;
while (!mx.empty()&&mx.front().id<idx)
mx.pop_front();
while (!mn.empty()&&mn.front().id<idx)
mn.pop_front();
}
ans=max(ans,i-idx+1);
}
printf("%d\n",ans);
}
}
return 0;
}
hdu 6228
【题意】
给出一棵有n个节点的树,现在你可以用k种颜色对节点染色,每种颜色对应一个集合,表示将树上所有这种颜色的点连起来经过的最小边。现在需要求所有集合取交集后的大小。
【思路】
假设我们取定1为根节点。
显然要是结果最大,相同颜色应该要尽可能分布在树的顶部和底部。
虽然我们需要考虑的是边,但是我们可以转化为对每个节点去考虑。令在这个节点的两侧每种颜色均有分布,那么一定会有一条公共边。
先用DFS求出每个点的子孙节点个数(包括自身),假设为x,那么它的上面点的个数为n-x,只要x>=k&&(n-x)>=k即能满足上面的条件。
我们只要枚举每个点,满足条件点的个数即为答案。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<int>v[200005];
int siz[200005];
int ans=0;
int n,k;
void dfs(int rt,int now)
{
siz[now]++;
for(int i=0;i<v[now].size();i++)
{
if(v[now][i]!=rt)
{
dfs(now,v[now][i]);
siz[now]+=siz[v[now][i]];
}
}
if(siz[now]>=k&&n-siz[now]>=k)
{
ans++;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
ans=0;
for(int i=0;i<=n;i++)
{
siz[i]=0;
v[i].clear();
}
int x,y;
for(int i=0;i<n-1;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1,1);
printf("%d\n",ans);
}
return 0;
}
树的重心:
树的重心就是指,树上一节点其最大子树的节点数最少的点。
对于一棵树,任选一个点作为根,进行一遍dfs,记录size[i]即该点为根时子树节点数为多少,记录子树节点数最多的点跟个数,同时,跟父节点的节点个数n-size[i]进行比较,就可求出该点最大子树节点个数。
const int M=1e5+10;
vector<int>v[M];
int siz[M],maxson[M],MX=0,rt;
void dfs(int now,int fa)
{
int maxson[now]=0;
siz[now]=1;
for(int i=0;i<v[now].size();i++)
{
if(v[now][i]!=fa)
{
dfs(now,v[now][i]);
siz[now]+=siz[v[now][i]];
maxson[now]=max(maxson[now],siz[v[now][i]]);
}
}
maxson[now]=max(maxson[now],n-siz[now]);
if(maxson[now]<MX){
rt=now;
MX=maxson[now];
}
}
树的重心的应用(hdu 4118):
给出一颗树,现在把所有的点的人都交换,保证每个地方只有一个人,且任何人都不在自己原来的那个点上,问交换的过程中所有人走的最远的距离是多少。
走的距离和最大,那么就是求树的重心,再求二倍的所有点到重心的距离和,树的重心的性质就是树上所有点到重心的距离和是最小的。
代码:
#include<bits/stdc++.h>//由点少的一边,走到点多的一变就是距离最大的走法
using namespace std;
const int M=1e5+10;
vector<int>v[M],w[M];
int siz[M],maxson[M],MX=M+10,rt;
long long ans=0;
int n;
void dfs(int now,int fa)
{
maxson[now]=0;
siz[now]=1;
for(int i=0;i<v[now].size();i++)
{
if(v[now][i]!=fa)
{
dfs(v[now][i],now);
siz[now]+=siz[v[now][i]];
maxson[now]=max(maxson[now],siz[v[now][i]]);
}
}
maxson[now]=max(maxson[now],n-siz[now]);
if(maxson[now]<MX){
rt=now;
MX=maxson[now];
}
}
void dfs1(int now,int rt,long long val)
{
//siz[now]=1;
ans+=val*2;
for(int i=0;i<v[now].size();i++)
{
if(v[now][i]!=rt)
{
dfs1(v[now][i],now,w[now][i]+val);
// siz[now]+=siz[v[now][i].to];
}
}
}
int main()
{
int tt,cas=1;
scanf("%d",&tt);
while(tt--)
{
MX=M+10;
memset(siz,0,sizeof(siz));
memset(maxson,0,sizeof(maxson));
scanf("%d",&n);
ans=0;
for(int i=1;i<=n;i++)
v[i].clear(),w[i].clear();
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
v[x].push_back(y);
v[y].push_back(x);
w[x].push_back(z);
w[y].push_back(z);
}
dfs(1,1);
dfs1(rt,rt,0);
printf("Case #%d: %I64d\n",cas++,ans);
}
return 0;
}
树形dp:
#include<bits/stdc++.h>//由点少的一边,走到点多的一变就是距离最大的走法
using namespace std;
vector<int> e[100005],w[100005];
long long ans;
int n;
int son[100005];
void dfs(int u,int father)
{
son[u]=1;
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i];
if(v==father)
continue;
dfs(v,u);
son[u]+=son[v];
int num=min(son[v],n-son[v]);
ans+=(long long)(num*w[u][i]*2);
}
}
int main()
{
int tt,cas=1;
scanf("%d",&tt);
while(tt--)
{
memset(son,0,sizeof(son));
scanf("%d",&n);
ans=0;
for(int i=1;i<=n;i++)
e[i].clear(),w[i].clear();
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
e[x].push_back(y);
e[y].push_back(x);
w[x].push_back(z);
w[y].push_back(z);
}
dfs(1,-1);
printf("Case #%d: %I64d\n",cas++,ans);
}
return 0;
}
树的最长路径:
const int M=1e5+10;
vector<pair<int,int>>v[M];
int d[M],maxson[M],MX=0,rt;
void dfs(int now,int rt)
{
d[now]=0;
int max1=0,max2=0;
for(int i=0;i<v[now].size();i++)
{
if(v[now][i].first!=rt)
{
dfs(v[now][i].first,now);
if(d[now]+v[now][i].second>max1)
{
max2=max1;
max1=d[now]+v[now][i].second;
}
else if(d[now][i]+v[now][i].second>max2)
{
max2=d[now]+v[now][i].second;
}
}
}
d[now]=max1;
return max1+max2;
}