整理的算法模板合集: ACM模板
树的直径满足如下性质:
若有多条直径,则所有的直径之间皆有公共点。
直径的两端一定是叶子。
树中距离某一直径端点最远的点,至少有一个是该直径的另一个端点。
对于树上任意一个点,与之距离最远的一个点,至少有一个直径的端点。
给你一个无权无向的树。编写程序以输出该树中最长路径(从一个节点到另一个节点)的长度。在这种情况下,路径的长度是我们从开始到目的地的遍历边数。
O ( n ) O(n) O(n)
const int N = 50007, M = 500007, INF = 0x3f3f3f3f;
int n, m, F[N], D[N], ans ;
int ver[N], nex[M], head[N], edge[N], tot;
bool vis[N];
void add(int x, int y, int z)
{
ver[tot] = y;
edge[tot] = z;
nex[tot] = head[x];
head[x] = tot ++ ;
}
void dp(int x){
vis[x] = 1;
for(int i = head[x] ;~i; i =nex[i]){
int y = ver[i], z = edge[i];
if(vis[y])continue;
dp(y);
ans = max(ans, D[x] + D[y] + z);
D[x] = max(D[x], D[y] + z);
}
}
int main(){
scanf("%d", &n);
memset(head, -1, sizeof head);
for(int i = 1; i < n; ++ i){
int x, y;
scanf("%d%d", &x, &y);
add(x, y, 1), add(y, x, 1);
}
dp(1);
printf("%d\n", ans);
return 0;
}
O ( n ) O(n) O(n)
//两次dfs一次求P一次求Q
void dfs(int u,int &ed){
if(dis[u] > ans)ans = dis[u],ed = u;
vis[u] = 1;
for(int i = head[u];~i;i = nex[i]){
int v = ver[i],w = edge[i];
if(vis[v])continue;
dis[v] = dis[u] + w;
dfs(v,ed);
}
return ;
}
int p,q;
void solve(){
dfs(1,p);
ans = dis[p] = 0;
memset(vis,0,sizeof vis);
dfs(p,q);
cout << ans << endl;
}
在一棵无根树上,找 A , B , C A,B,C A,B,C三个点,使得 A B + B C ( A C > B C ) AB+BC(AC>BC) AB+BC(AC>BC)或 A C + A B ( B C > A C ) AC+AB(BC>AC) AC+AB(BC>AC)最大。
我们假设 A C > B C AC>BC AC>BC,那么答案就是找到最大 A B + B C AB+BC AB+BC简单贪心一下,先令AB最大,然后再找一个相应的最大的BC,强调要满足(AC>AB)所以我们首先找出树的直径,然后枚举除了两个端点外的点 C C C,使得 m i n ( A C , B C ) min(AC,BC) min(AC,BC)最大,答案就是树的直径 + m i n ( A C , B C ) +min(AC,BC) +min(AC,BC)
typedef long long ll;
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;
int n, m;
ll d[N], copy_d[N];
bool vis[N];
int head[N], ver[M], nex[M], edge[M], tot;
void add(int x, int y, int z)
{
ver[tot] = y;
edge[tot] = z;
nex[tot] = head[x];
head[x] = tot ++ ;
}
int bfs(int S)
{
queue<int>q;
memset(d, 0, sizeof d);
memset(vis, 0, sizeof vis);
//while(q.size())q.pop();
q.push(S), vis[S] = 1;
int maxx = 0, maxid;
while(q.size()){
int x = q.front();
q.pop();
for(int i = head[x]; ~i; i = nex[i]){
int y = ver[i], z = edge[i];
if(vis[y])continue;
vis[y] = 1;
d[y] = d[x] + z;
q.push(y);
if(d[y] > maxx){
maxx = d[y];
maxid = y;
}
}
}
return maxid;
}
int main()
{
scanf("%d%d", &n, &m);
memset(head, -1, sizeof head);
for(int i = 1; i <= m; ++ i){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
int p, q;
p = bfs(1);
q = bfs(p);
ll ans = d[q];//the length of AB
for(int i = 1; i <= n; ++ i){
copy_d[i] = d[i];
}
bfs(q);
ll res = 0;
for(int i = 1; i <= n; ++ i){
res = max(res, min(d[i], copy_d[i]));//find the longest edges among the remaining edges
}
ans += res;
printf("%lld\n", ans);
return 0;
}
有一个n个点的带权无向树,q次操作,每次修改一条边的权值,要求在每次修改后,输出树的直径大小,强制在线。
/*lmx,rmx,lmi,rmi,ld,rd,lrd,ans前缀/后缀的最大/最小和,前缀右减左最大,
后缀右减左最大,两端点必须选满右减左最大,任意子串右减左最大*/
#include
#define ls(a) ((a)<<1)
#define rs(a) ((a)<<1|1)
#define N 200001
#define int long long
using namespace std;
typedef long long ll;
#define G if(++ip==ie)if(fread(ip=ibuf,1,LL,stdin))
const int LL=1<<19;
char ibuf[LL],*ie=ibuf+LL,*ip=ie-1;
inline char nc(void){
G;return *ip;}
//#define getchar nc
inline ll read(void){
char opt;ll flag=1,res=0;
while((opt=getchar())<'0'||opt>'9')if(opt=='-')flag=-1;
while(opt>='0'&&opt<='9'){
res=(res<<3)+(res<<1)+opt-'0';opt=getchar();}
return res*flag;
}
int n,m,st[N],top,q;
struct edge{
int to;ll v;int x;};vector<edge>g[N];
ll w,sum[N<<2],lmx[N<<2],rmx[N<<2],lmi[N<<2],rmi[N<<2],ld[N<<2],rd[N<<2],lrd[N<<2],ans[N<<2],lastans,p[N][2];
void pushup(int x){
sum[x]=sum[ls(x)]+sum[rs(x)];
lmx[x]=max(lmx[ls(x)],sum[ls(x)]+lmx[rs(x)]);
rmx[x]=max(rmx[rs(x)],sum[rs(x)]+rmx[ls(x)]);
lmi[x]=min(lmi[ls(x)],sum[ls(x)]+lmi[rs(x)]);
rmi[x]=min(rmi[rs(x)],sum[rs(x)]+rmi[ls(x)]);
ld[x]=max(ld[ls(x)],max(ld[rs(x)]-sum[ls(x)],lrd[ls(x)]+lmx[rs(x)]));
rd[x]=max(rd[rs(x)],max(sum[rs(x)]+rd[ls(x)],lrd[rs(x)]-rmi[ls(x)]));
lrd[x]=max(lrd[ls(x)]+sum[rs(x)],lrd[rs(x)]-sum[ls(x)]);
ans[x]=max(max(ans[ls(x)],ans[rs(x)]),max(ld[rs(x)]-rmi[ls(x)],rd[ls(x)]+lmx[rs(x)]));
}
inline void Build(int pos,int l,int r){
ll v;if(l==r)return sum[pos]=(v=st[l]),lmx[pos]=rmx[pos]=max(v,0ll),lmi[pos]=rmi[pos]=min(v,0ll),ld[pos]=rd[pos]=lrd[pos]=ans[pos]=v,void();
int mid=(l+r)>>1;
Build(ls(pos),l,mid),Build(rs(pos),mid+1,r);
pushup(pos);
}
inline void Change(int pos,int l,int r,int x,ll v){
if(l==r)return sum[pos]=v,lmx[pos]=rmx[pos]=max(v,0ll),lmi[pos]=rmi[pos]=min(v,0ll),ld[pos]=rd[pos]=lrd[pos]=ans[pos]=v,void();
int mid=(l+r)>>1;
(x<=mid)?Change(ls(pos),l,mid,x,v):Change(rs(pos),mid+1,r,x,v);
pushup(pos);
}
inline void dfs(int x,int prt){
int y;for(auto t:g[x])if((y=t.to)^prt)st[*p[t.x]=++top]=t.v,dfs(y,x),st[p[t.x][1]=++top]=-t.v;
}
signed main(void){
int i,x;ll y,z;n=read(),q=read(),w=read();
for(i=1;i<n;++i)x=read(),y=read(),z=read(),g[x].push_back({
y,z,i}),g[y].push_back({
x,z,i});
dfs(1,0);Build(1,1,top);
while(q--){
x=(read()+lastans)%(n-1)+1,y=(read()+lastans)%w;
Change(1,1,top,*p[x],y),Change(1,1,top,p[x][1],-y);
printf("%lld\n",lastans=ans[1]);
}
return 0;
}
重心的性质
给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数size,如果size相同就选取编号最小的.
const int N = 50007, M = 500007, INF = 0x3f3f3f3f;
typedef long long ll;
int n, m;
int head[N], ver[M], nex[M], tot;
ll T;
int son[N], ans, pos;
bool vis[N];
void add(int x, int y)
{
ver[tot] = y;
nex[tot] = head[x];
head[x] = tot ++ ;
}
void dfs(int x)
{
vis[x] = 1, son[x] = 1;
int res = 0;
for(int i = head[x]; ~i; i = nex[i]){
int y = ver[i];
if(!vis[y]){
dfs(y);
son[x] += son[y];
res = max(res, son[y]);//x下面的子树
}
}
res = max(res, n - son[x]);//x上面的子树
if(res < ans || (res == ans && x < pos)){
ans = res, pos = x;
}
}
int main()
{
scanf("%lld", &T);
while(T -- ){
tot = 0;
memset(head, -1, sizeof head);
memset(vis, 0, sizeof vis);
ans = INF;
scanf("%d", &n);
for(int i = 1; i < n; ++ i){
int x, y;
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
dfs(1);
printf("%d %d\n", pos, ans);
}
return 0;
}