[HNOI2016] 网络

前言

我不期望谁能看懂,写博客只是因为写这道题过程曲折,需要博客记录。

人话:需要写博客平静自己的心情。

题目

洛谷

LibreOJ

讲解

这道题思路不难。

为了方便讲解,我们将题目中的交互信息叫做路径,其重要度叫做该路径的权值

首先我们考虑如果有很多路径已经挂在了树上,我们怎么回答询问?

考虑二分。

直接二分答案,判断合法性的时候直接将可能的路径求交集,然后判断询问的点是否在该路径交集上即可。

多个路径求交集可以将路径按权值排序后放到线段树上维护。

由于此题需要求大量的 \(\tt LCA\),所以我们需要高效的 \(\tt ST\) 来求 \(O(1)\) 求。

听着很简单是不是?等你写路径交集函数的时候就不会这么觉得了。

当然这道题也可以用整体二分做。

代码

//12252024832524
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 200005;
int n,m;

LL Read()
{
    LL x = 0,f = 1;char c = getchar();
    while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    return x * f;
}
TT void Put1(T x)
{
    if(x > 9) Put1(x/10);
    putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
    if(x < 0) putchar('-'),x = -x;
    Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int head[MAXN],tot; 
struct edge
{
    int v,nxt;
}e[MAXN << 1];
void Add_Edge(int x,int y) 
{
    e[++tot].v = y;
    e[tot].nxt = head[x];
    head[x] = tot;
}
void Add_Double_Edge(int x,int y) 
{
    Add_Edge(x,y);
    Add_Edge(y,x);
}
int dfn[MAXN],dfntot,st[MAXN << 1][19],cnt,who[MAXN],lg[MAXN << 1],d[MAXN];
int cs(int x,int y){return d[x] > d[y] ? y : x;}
int cs2(int x,int y){return d[x] > d[y] ? x : y;}
void dfs(int x,int fa)
{
    d[x] = d[fa] + 1; 
    dfn[x] = ++dfntot;
    st[who[x] = ++cnt][0] = x;
    for(int i = head[x]; i ;i = e[i].nxt)
    {
        if(e[i].v == fa) continue;
        dfs(e[i].v,x);
        st[++cnt][0] = x;
    }
}
int opt[MAXN][4];
int lca(int x,int y)
{
    int u = who[x],v = who[y];
    if(u > v) swap(u,v);
    int ll = lg[v-u+1];
    return cs(st[u][ll],st[v-(1<<ll)+1][ll]);
}

int netot;
struct Network
{
    int x,val;
    Network(){}
    Network(int x1,int val1){
        x = x1;
        val = val1;
    }
    bool operator < (const Network &px)const{
        return val < px.val;
    }
}net[MAXN];
int to[MAXN];
struct node
{
    int u,v;
    node(){}
    node(int u1,int v1){
        u = u1;
        v = v1;
    }
    void fk(){if(dfn[u] > dfn[v]) swap(u,v);}
}t[MAXN << 2];
bool on(int x,node p)
{
	if(p.u < 0) return 0;
    int LCA = lca(p.u,p.v);
    if(lca(x,LCA) != LCA) return 0;
    p.fk();
	int l1 = lca(p.u,x),l2 = lca(p.v,x);
	if((l1 == LCA && l2 == x) || (l1 == x && l2 == LCA)) return 1;
	return 0;
}
node merg(node p1,node p2)
{
    if(!p1.u) return p2;
    if(!p2.u) return p1;
    if(p1.u < 0 || p2.u < 0) return node(-1,-1);
	int LCA1 = lca(p1.u,p1.v),LCA2 = lca(p2.u,p2.v),cn1 = 0,cn2 = 0;
	if(!on(LCA1,p2) && !on(LCA2,p1)) return node(-1,-1);
	p1.fk(); p2.fk();
	if(LCA1 == p1.u) cn1 = 1;
	if(LCA2 == p2.u) cn2 = 1;
	if(cn2) swap(p1,p2),swap(LCA1,LCA2),swap(cn1,cn2);
	if(cn1 && !cn2)
	{
		if(cn2)//chain & chain
			return node(dfn[p1.u] > dfn[p2.u] ? p1.u : p2.u,dfn[p1.v] < dfn[p2.v] ? p1.v : p2.v);
		else//chain & turning
		{
			int V = cs2(cs2(lca(p1.u,p2.u),lca(p1.u,p2.v)),cs2(lca(p1.v,p2.u),lca(p1.v,p2.v)));
			return node(on(LCA2,p1) ? LCA2 : p1.u,V);
		}
	}
	else//turning & turning
	{
		if(LCA1 == LCA2) 
		{
			node ret1 = node(lca(p1.u,p2.u),lca(p1.v,p2.v));
			node ret2 = node(lca(p1.u,p2.v),lca(p1.v,p2.u));
			if(ret1.u == ret1.v) return ret2;
			return ret1;
		}
		int U,V;
		if(on(LCA1,p2)) U = LCA1;
		else U = LCA2;
		V = cs2(cs2(lca(p1.u,p2.u),lca(p1.u,p2.v)),cs2(lca(p1.v,p2.u),lca(p1.v,p2.v)));
		return node(U,V); 
	}
}
#define lc (x<<1) 
#define rc (x<<1|1) 
struct SegmentTree
{
	int ex[MAXN << 2];
	
    void up(int x){t[x] = merg(t[lc],t[rc]);ex[x] = ex[lc] + ex[rc];}

    void Add(int x,int l,int r,int pos,node ad)
    {
        if(l == r) {t[x] = ad;ex[x] = 1;return;}
        int mid = (l+r) >> 1;
        if(pos <= mid) Add(lc,l,mid,pos,ad);
        else Add(rc,mid+1,r,pos,ad);
        up(x);
    }

    void Del(int x,int l,int r,int pos)
    {
        if(l == r) {t[x] = node(0,0);ex[x] = 0;return;}
        int mid = (l+r) >> 1;
        if(pos <= mid) Del(lc,l,mid,pos);
        else Del(rc,mid+1,r,pos);
        up(x);
    }

    int Query(int x,int l,int r,int pos,node now)
    {
        if(l == r) return net[l].val;
        node n1 = merg(now,t[rc]);
        int mid = (l+r) >> 1;
        if(!on(pos,n1) && ex[rc]) return Query(rc,mid+1,r,pos,now);
        return Query(lc,l,mid,pos,n1);
    }
}ST;

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    n = Read(); m = Read();
    for(int i = 1;i < n;++ i) Add_Double_Edge(Read(),Read());
    dfs(1,0);
    lg[0] = -1;
    for(int i = 1;i <= cnt;++ i) lg[i] = lg[i>>1] + 1;
    for(int i = 1;i <= lg[cnt];++ i)
        for(int j = 1;j+(1<<i)-1 <= cnt;++ j)
            st[j][i] = cs(st[j][i-1],st[j+(1<<(i-1))][i-1]);
    for(int i = 1;i <= m;++ i)
    {
        opt[i][0] = Read();
        if(!opt[i][0]) 
        {
            for(int j = 1;j <= 3;++ j) opt[i][j] = Read();
            net[++netot] = Network(i,opt[i][3]);
        }
        else opt[i][1] = Read();
    }
    sort(net+1,net+netot+1);
    for(int i = 1;i <= netot;++ i) to[net[i].x] = i;
    for(int i = 1;i <= m;++ i)
    {
        if(!opt[i][0]) ST.Add(1,1,netot,to[i],node(opt[i][1],opt[i][2]));
        else if(opt[i][0] == 1) ST.Del(1,1,netot,to[opt[i][1]]);
        else 
        {
            if(!ST.ex[1] || on(opt[i][1],t[1])) Put(-1,'\n');
            else Put(ST.Query(1,1,netot,opt[i][1],node(0,0)),'\n');
        }
    }
    return 0;
}

在画了一万张路径相交的图之后,终于写出了这份代码。

此代码已经经过精简(指删掉调试信息),但仍然有215行。

似乎删掉调试信息之前有260行+。

posted @ 2021-07-15 22:05  皮皮刘  阅读(29)  评论(0编辑  收藏  举报