学习笔记-基环树入门

定义:多一条边的树。

性质:有一个环,删掉这环的任意一条边就可以变成树。

题目做法:一般先求出环,然后把处理这个环上每一个节点的子树,最后处理这个环。

模板题:给定一棵基环树,边带权,求直径。

做法1:刚开始没有接触过基环树的问题,所以不知道怎么搞,不过乱搞一波后发现nlogn是可写的。首先,dfs找到基环树的那个环,然后处理环上每个点的子树,维护这个子树里的最长链和次长链,更新答案。然后考虑环上两点的贡献,发现是它们之间的距离加上它们各自子树最长链的的和。而这个东西暴力枚举是n^2的(因为环最大是n),因此考虑优化。这里有一个常用的小技巧,拆环,将环倍长维护前缀和,如1234变为1234123,这样就可以保证这个环无论怎么转它的顺序都对应这个拆出来的链上的一个长为原本链长l的区间。这样处理完之后我们发现对于i,j(j < i <= l)两点,它们之间的距离就是f(i,j)=Min(a[i]+a[j]+s[i]-s[j],a[i]+a[j]+s[j+l]-s[i]),这里ai表示环上i这个点子树中最大链长,si表示环上到i点距离前缀和,l代表环倍长前长度。而因为我们要求直径,所以我们要找到一对i,j使这个式子取到最大值。因为这里有一个Min所以很难处理,那么我们将Min拆开,原式变为:当2*s[i]<=s[j+l]+s[j]时

f(i,j)=a[i]+a[j]+s[i]-s[j],当2*s[i]>=s[j+l]+s[j]时f(i,j)=a[i]+a[j]+s[j+l]-s[i].这样我们就能乱搞了,对于所有i(i <= l )将每个i的s[i]*2,s[i]+s[i+l]都扔到一个数组里,sort,离散化,然后开两棵线段树,一边枚举式子里的i一边维护式子里j最大值即可,复杂度nlogn(出题人貌似没认真卡,1e6跑的飞快)。

代码:

#include
#define ll long long
using namespace std;
const int N=1e6+10;
const ll inf=1e18;
const int MAXSIZE = 1 << 22;
inline char gc()
{
    static char In[MAXSIZE], *at = In, *en = In;
    if (at == en)
    {
        en = (at = In) + fread(In, 1, MAXSIZE, stdin);
    }
    return at == en ? EOF : *at++;
}
inline long long gt()
{
    char c;
    while (c = gc(), !(c >= '0'&&c <= '9') && c != '-') {}
    bool f = c == '-';
    long long x = f ? 0 : c - '0';
    for (c = gc(); c >= '0'&&c <= '9'; c = gc())
    {
        x = x * 10 + c - '0';
    }
    return f ? -x : x;
}
 
int n,tot=-1;
int hd[N*2],nxt[N*2],to[N*2],w[N*2],fa[N],dis[N],huan[N*2],szh;
bool vis[N],used[N];
ll sum[N*2],ans=0,max1[N],max2[N],seg1[N*8],seg2[N*8],tax[N*2];
struct gg{
    ll a,s,ss;
    int ps,pss;
}node[N];
 
void add(int u,int v,int ww)
{
    nxt[++tot]=hd[u],to[tot]=v,w[tot]=ww,hd[u]=tot;
    nxt[++tot]=hd[v],to[tot]=u,w[tot]=ww,hd[v]=tot;
}
void get_huan(int up,int dw,int cost)
{
    int nw=dw,bf=0,tmp;
    while(1)
    {
        huan[++szh]=nw;
        if(szh>1)sum[szh]=sum[szh-1]+dis[bf];
        bf=nw;
        if(nw==up)break;
        else nw=fa[nw];
    }
    tmp=szh;
    for(int i=1;imax1[pos])swap(max1[pos],max2[pos]);
    }
    ans=max(ans,max1[pos]+max2[pos]);
    return;
}
void push_up1(int k)
{seg1[k]=max(seg1[k<<1],seg1[k<<1|1]);}
void push_up2(int k)
{seg2[k]=max(seg2[k<<1],seg2[k<<1|1]);}
void cg1(int l,int r,int to,int k,ll cc)
{
    if(l==r)
    {seg1[k]=max(seg1[k],cc);return;}
    int mid=(l+r)>>1;
    if(to<=mid)cg1(l,mid,to,k<<1,cc);
    else cg1(mid+1,r,to,k<<1|1,cc);
    push_up1(k);
}
void cg2(int l,int r,int to,int k,ll cc)
{
    if(l==r)
    {seg2[k]=max(seg2[k],cc);return;}
    int mid=(l+r)>>1;
    if(to<=mid)cg2(l,mid,to,k<<1,cc);
    else cg2(mid+1,r,to,k<<1|1,cc);
    push_up2(k);
}
ll qry1(int L,int R,int l,int r,int k)
{
    if(l>R||r>1;
    return max(qry1(L,R,l,mid,k<<1),qry1(L,R,mid+1,r,k<<1|1));
}
ll qry2(int L,int R,int l,int r,int k)
{
    if(l>R||r>1;
    return max(qry2(L,R,l,mid,k<<1),qry2(L,R,mid+1,r,k<<1|1));
}
int main()
{
    int u,v,ww;
//  freopen("darksoul.in","r",stdin);
//  freopen("darksoul.out","w",stdout);
    n=gt();
    memset(hd,-1,sizeof hd);
    memset(nxt,-1,sizeof nxt);
    for(int i=1;i<=n;i++)
    {
        u=gt(),v=gt(),ww=gt();
        add(u,v,ww);
    }
    dfs_huan(1,-1);
    int m=szh/2+1,l=0;
    for(int i=1;i<=szh;i++)
        used[huan[i]]=1;
    for(int i=1;i<=m;i++)
        dfs(huan[i],-1);
    for(int i=1;i<=m;i++)
    {
        node[i].a=max1[huan[i]];
        node[i].s=sum[i];
        if(i

做法2(标准做法):单调队列。

我们不要只考虑倍长数组的前l各元素,而是直接考虑整一个,那么所求就是两点距离为s[i]-s[j] ( j < i 同时这个值要小于整个环总长一半,不然就可以往另一边走),而我们可以发现前缀和s数组一定是递增的,不妨设环总长是L,那么当枚举到的i的2*(s[i]-s[j])>L,则该j不再可行,而s递增,那么先进队一定先出,所以显然可以用单调队列来维护了,权值是a[j]-s[j],同时再维护一个id是s[j],枚举到i时从队首一直把与它相距大于L/2的pop,在取队首用队首权值+s[i]+a[i]更新答案即可。

代码:

#include
#define ll long long
using namespace std;
const int N=1e6+10;
const ll inf=1e18;
const int MAXSIZE = 1 << 22;
inline char gc()
{
    static char In[MAXSIZE], *at = In, *en = In;
    if (at == en)
    {
        en = (at = In) + fread(In, 1, MAXSIZE, stdin);
    }
    return at == en ? EOF : *at++;
}
inline long long gt()
{
    char c;
    while (c = gc(), !(c >= '0'&&c <= '9') && c != '-') {}
    bool f = c == '-';
    long long x = f ? 0 : c - '0';
    for (c = gc(); c >= '0'&&c <= '9'; c = gc())
    {
        x = x * 10 + c - '0';
    }
    return f ? -x : x;
}
 
int n,tot=-1;
int hd[N*2],nxt[N*2],to[N*2],w[N*2],fa[N],dis[N],huan[N*2],szh;
bool vis[N],used[N];
ll sum[N*2],ans=0,max1[N],max2[N],a[N],s[N];
struct hh{
	ll w,s;
	hh(){};
	hh(ll w,ll s):w(w),s(s){};
};
dequeq;
 
void add(int u,int v,int ww)
{
    nxt[++tot]=hd[u],to[tot]=v,w[tot]=ww,hd[u]=tot;
    nxt[++tot]=hd[v],to[tot]=u,w[tot]=ww,hd[v]=tot;
}
void get_huan(int up,int dw,int cost)
{
    int nw=dw,bf=0,tmp;
    while(1)
    {
        huan[++szh]=nw;
        if(szh>1)sum[szh]=sum[szh-1]+dis[bf];
        bf=nw;
        if(nw==up)break;
        else nw=fa[nw];
    }
    tmp=szh;
    for(int i=1;imax1[pos])swap(max1[pos],max2[pos]);
    }
    ans=max(ans,max1[pos]+max2[pos]);
    return;
}
void pu(hh nw)
{
	while(!q.empty())
	{
		if(q.back().w<=nw.w)q.pop_back();
		else break;
	}
	q.push_back(nw);
}
int main()
{
    int u,v,ww;
    n=gt();
    memset(hd,-1,sizeof hd);
    memset(nxt,-1,sizeof nxt);
    for(int i=1;i<=n;i++)
    {
        u=gt(),v=gt(),ww=gt();
        add(u,v,ww);
    }
    dfs_huan(1,-1);
    int m=szh/2+1,l=0;
    for(int i=1;i<=szh;i++)
        used[huan[i]]=1;
    for(int i=1;i<=m;i++)
        dfs(huan[i],-1);
    for(int i=1;i<=szh;i++)
    {
    	a[i]=max1[huan[i]];
    	s[i]=sum[i];
    }
    int j;ll L=sum[m+1]-sum[1];hh nw;
    for(int i=1;i<=szh;i++)
    {
    	while(!q.empty())
    	{
    		if(2*(s[i]-q.front().s)>L)q.pop_front();
    		else break;
		}
		if(!q.empty())ans=max(ans,a[i]+s[i]+q.front().w);
		pu(hh(a[i]-s[i],s[i]));
	}
    printf("%lld",ans+1);
}

(ps:以上两代码如需运行均要用文件输入输出)

你可能感兴趣的:(笔记,训练集)