• 2021牛客暑期多校训练营7 F-xay loves trees


    没有传送门。


    题面大意:给你两棵树,根都是1号节点。让你找一个最大的节点集合,满足:
    1.在第一棵树上,集合中的节点是相连的,且任意连个节点之间是祖孙关系。
    2.在第二棵树上,任意两个节点直接都不是祖孙关系。


    这题当时想出来了,结果有一个数组忘清空,先WA后RE,到最后也没查出来。


    首先根据第一个条件,这些点一定某一个节点到根的链的一段。那么一种思路是处理出每一个节点(u)往上最高能往上走几步(即(ans[u])),最后取最大值。而且对于一条边((u,v))(u)(v)的父亲),必有(ans[v] leqslant ans[u] + 1).

    那对于一个节点,如何求出(ans[u])呢?我现在会两种做法:


    第一种做法:
    答案显然具有单调性,因此我们对于每一个节点,二分其能向上走的步数。那么对于一个二分值(mid),应该如何检验?即怎么判断这些点互相不是祖孙关系。其实就是将第二棵树的dfs序求出来,那么每一个点的dfs序区间就是([dfn[u], dfn[u] + siz[u] - 1]),只要判断这些区间不相交即可。

    如果是到根的路径,可以用线段树实现区间修改和查询区间和是否为(0);现在改成了一段,那么就能想到用主席树。虽然主席树不支持区间修改,但是可以这么解决:建立两棵主席树,一棵叫“祖先树”,一棵叫“儿子树”。

    祖先树专门用来判断(u)是否为某些点的祖先,那么只要查询([dfn[u], dfn[u] +siz[u] - 1])这段区间和是否为(0)。而修改恰好是单点修改,将(dfn[u])标记成(1)即可。

    儿子树专门用来判断(u)是否为某些点的儿子,如果单点查询(dfn[u])的值是否为(0),那修改就要区间修改。因此我们修改的时候用差分,查询的时候查询前缀和就行了!

    这样两棵主席树都是区间(前缀)查询和单点修改,主席树就能胜任了。时间复杂度(O(nlog^2n)),能过。(比赛的时候是倍增的数组忘清空了……)


    第二种做法:
    上面提到主席树无法区间修改,是因为标记无法下传(会下传到被继承的原来的节点)。但是如果用“标记永久化”就可以实现了。

    标记永久化好久没写,完全忘掉了。其思路就是将改变的值的贡献直接算到区间里,在递归边界时打标记。但是标记不下传,而是在查询的时候,算上标记的贡献。

    对于这道题,我们可以用标记永久化维护覆盖某一个区间的最深的点的深度,那么这个深度之下必然没有点可以覆盖这一dfs序的区间。因此我们建立一棵树,查询的时候就查询该dfs序区间被覆盖的最大深度,修改的时候就进行区间修改,更新最大值。

    而且该算法不用二分,因为我们需要的就是前缀查询,标记永久化刚好可以胜任。这样时间复杂度就是(O(nlogn))了。


    一下给出两份代码:
    第一种做法:

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("")
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    const int maxn = 3e5 + 500;
    const int maxt = 1.2e7 + 5;
    const int N = 18;
    ll read() {ll x; scanf("%lld", &x); return x;}
    void write(ll x) {printf("%lld", x);}
     
    int n;
    struct Edge
    {
        int nxt, to;
    }e[maxn << 1];
    int head[maxn], ecnt = -1;
    In void addEdge(int x, int y)
    {
        e[++ecnt] = (Edge){head[x], y};
        head[x] = ecnt;
    }
    Edge e2[maxn << 1];
    int head2[maxn], ecnt2 = -1;
    In void addEdge2(int x, int y)
    {
        e2[++ecnt2] = (Edge){head2[x], y};
        head2[x] = ecnt2;
    }
     
    int dfsx[maxn], siz[maxn], cnt = 0;
    In void dfs2(int now, int _f)
    {
        siz[now] = 1;
        dfsx[now] = ++cnt;
        for(int i = head2[now], v; ~i && (v = e2[i].to); i = e2[i].nxt)
        {
            if(v == _f) continue;
            dfs2(v, now);
            siz[now] += siz[v];
        }
    }
     
    const int P = maxn - 20;
    struct Tree
    {
        int ls = 0, rs = 0, sum = 0;
        In void init() {ls = rs = sum = 0;}
    };
    struct trees
    {
        Tree t[maxt];
        int root[maxn << 1], cnt;
        In void init() {Mem(root, 0), cnt = 0;}
        In void insert(int old, int& now, int l, int r, int x, int d)
        {
            t[now = ++cnt] = t[old];
            if(x < l || x > r) return;
            t[now].sum += d;
            if(l == r) return;
            int mid = (l + r) >> 1;
            if(x <= mid) insert(t[old].ls, t[now].ls, l, mid, x, d);
            else insert(t[old].rs, t[now].rs, mid + 1, r, x, d);
        }
        In int query(int old, int now, int l, int r, int L, int R)
        {
            if(!old && !now) return 0;
            if(l == L && r == R) return t[now].sum - t[old].sum;
            int mid = (l + r) >> 1;
            if(R <= mid) return query(t[old].ls, t[now].ls, l, mid, L, R);
            else if(L > mid) return query(t[old].rs, t[now].rs, mid + 1, r, L, R);
            else return query(t[old].ls, t[now].ls, l, mid, L, mid) + query(t[old].rs, t[now].rs, mid + 1, r, mid + 1, R);
        }
    }tA, tS;
     
    int ha[maxn], dep[maxn], fa[N + 2][maxn];
    In int calc(int x, int len)
    {
        int sta = x;
        for(int i = ha[len]; i >= 0; --i)
        {
            int y = fa[i][x], z = fa[0][y];
            if(!y) continue;
            int tp1 = tA.query(tA.root[z], tA.root[fa[0][sta]], 1, n, dfsx[sta], dfsx[sta] + siz[sta] - 1);
            int tp2 = tS.query(tS.root[z], tS.root[fa[0][sta]], 1, n, 1, dfsx[sta]);
            if(!tp1 && !tp2) x = y;
        }
        return dep[sta] - dep[x];
    }
     
    int ans[maxn];
    In void dfs1(int now, int _f)
    {
        for(int i = 1; (1 << i) <= dep[now]; ++i)
            fa[i][now] = fa[i - 1][fa[i - 1][now]];
    	ans[now] = min(ans[_f] + 1, calc(now, ans[_f] + 1));
        tA.insert(tA.root[_f], tA.root[now], 1, n, dfsx[now], 1);
        tS.insert(tS.root[_f], tS.root[P], 1, n, dfsx[now], 1);
        tS.insert(tS.root[P], tS.root[now], 1, n, dfsx[now] + siz[now], -1);
        forE(i, now, v)
        {
            if(v == _f) continue;
            dep[v] = dep[now] + 1;
            fa[0][v] = now;
            dfs1(v, now);
        }
    }
     
    In void init()
    {
    	Mem(fa, 0);
        Mem(head, -1), ecnt = -1;
        Mem(head2, -1), ecnt2 = -1;
        cnt = 0;
        tA.init(), tS.init();
    }
     
    int main()
    {
        int T = read();
        while(T--)
        {
            init();
            n = read();
            for(int i = 1; i < n; ++i)
            {
                int x = read(), y = read();
                addEdge(x, y), addEdge(y, x);
            }
            for(int i = 1; i < n; ++i)
            {
                int x = read(), y = read();
                addEdge2(x, y), addEdge2(y, x);
            }
            dfs2(1, 0);
            for(int i = 2; i <= n; ++i) ha[i] = ha[i >> 1] + 1;
            dep[1] = 1, dfs1(1, 0);
            int Max = 0;
            for(int i = 1; i <= n; ++i) Max = max(Max, ans[i]);
            write(Max + 1), enter;
        }
        return 0;
    }
    

    比较长,还慢,还是看第二种做法吧。

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 3e5 + 5;
    const int maxt = 6e6 + 5;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    int n;
    #define pb push_back
    vector<int> v1[maxn], v2[maxn];
    
    int dfsx[maxn], siz[maxn], cnt = 0;
    In void dfs2(int now, int _f)
    {
    	siz[now] = 1, dfsx[now] = ++cnt;
    	for(auto v : v2[now])
    	{
    		if(v == _f) continue;
    		dfs2(v, now);
    		siz[now] += siz[v];
    	}
    }
    
    struct Tree
    {
    	int ls, rs, lzy, Max;
    }t[maxt];
    int root[maxn], tcnt = 0;
    In void insert(int old, int& now, int l, int r, int L, int R, int d)
    {
    	t[now = ++tcnt] = t[old];
    	t[now].Max = max(t[now].Max, d);
    	if(l == L && r == R) return (void)(t[now].lzy = max(t[now].lzy, d));
    	int mid = (l + r) >> 1;
    	if(R <= mid) insert(t[old].ls, t[now].ls, l, mid, L, R, d);
    	else if(L > mid) insert(t[old].rs, t[now].rs, mid + 1, r, L, R, d);
    	else insert(t[old].ls, t[now].ls, l, mid, L, mid, d), insert(t[old].rs, t[now].rs, mid + 1, r, mid + 1, R, d);
    }
    In int query(int now, int l, int r, int L, int R)
    {
    	if(l == L && r == R) return t[now].Max;
    	int ret, mid = (l + r) >> 1;
    	if(R <= mid) ret = query(t[now].ls, l, mid, L, R);
    	else if(L > mid) ret = query(t[now].rs, mid + 1, r, L, R);
    	else ret = max(query(t[now].ls, l, mid, L, mid), query(t[now].rs, mid + 1, r, mid + 1, R));
    	return max(ret, t[now].lzy);
    }
    
    int dep[maxn], f[maxn];
    In void dfs1(int now, int _f)
    {
    	f[now] = min(f[_f] + 1, dep[now] - query(root[_f], 1, n, dfsx[now], dfsx[now] + siz[now] - 1));
    	insert(root[_f], root[now], 1, n, dfsx[now], dfsx[now] + siz[now] - 1, dep[now]);
    	for(auto v : v1[now])
    	{
    		if(v == _f) continue;
    		dep[v] = dep[now] + 1;
    		dfs1(v, now);
    	}
    }
    
    In void init()
    {
    	for(int i = 1; i <= n; ++i) v1[i].clear(), v2[i].clear();
    	Mem(root, 0), cnt = tcnt = 0;
    }
    
    int main()
    {
    	int T = read();
    	while(T--)
    	{
    		n = read();
    		init();
    		for(int i = 1, x, y; i < n; ++i) x = read(), y = read(), v1[x].pb(y), v1[y].pb(x);
    		for(int i = 1, x, y; i < n; ++i) x = read(), y = read(), v2[x].pb(y), v2[y].pb(x);
    		dfs2(1, 0);
    		dep[1] = 1, dfs1(1, 0);
    		int Max = 0;
    		for(int i = 1; i <= n; ++i) Max = max(Max, f[i]);
    		write(Max), enter;
    	}
    	return 0;
    }
    
  • 相关阅读:
    python之bytes和string(转)
    http post请求传文件报错 invalid character '' in numeric literal
    正则表达式匹配从指定字符开始到指定字符结束的字符串
    为什么提倡会议上要多提问?
    4K 显示器突然变模糊了怎么调回来
    快速将 gif 图转成 jpg 图片
    <一>window安装git
    <二>使用git将本地项目上传到gitee
    <一>window安装rabbitmq
    <一>redis安装
  • 原文地址:https://www.cnblogs.com/mrclr/p/15120492.html
Copyright © 2020-2023  润新知