Codeforces Round 914 (Div. 2) F. Beautiful Tree(树链剖分/倍增+线段树优化区间建图+拓扑排序)

题目

n(n<=2e5)个点的一棵树,你需要将n个点的权值构造为一个[1,n]的排列,

并满足m(m<=2e5)个限制,限制分两种:

1 a b c,要求点a到点b的简单路径上,最小值位于点c处,保证c在a到b的简单路径上

2 a b c,要求点a到点b的简单路径上,最大值位于点c处,保证c在a到b的简单路径上

不能构造输出-1

思路来源

SSerxhs代码、官方题解代码

线段树优化建图

线段树优化建图(知识总结+板子整理)-CSDN博客

题解

感觉很典,就是三个板子拼到了一起的这么一个题

有大小关系限制,考虑拓扑排序,x->y连一条边,表示x需要小于y

考虑怎么暴力,暴力就是将链上的每个点i都和c之间连边,

c是最小值的话,c->i连边;

c是最大值的话,i->c连边

然后考虑怎么优化点的数量,

这就是线段树优化区间建图的思想,一棵入树,一棵出树,

c最小值的话,就在出树(自顶向下)上连;

c是最大值的话,就在入树(自底向上)上连

如果用树链剖分做的话,由于树链上是log条重链和轻点,每条链dfs序连续,

可以用现成的线段树优化区间建图的板子,每棵树的点的总数是4n

一条链可以映射到log个点上,每个限制连接log^2个点,最后对所有限制跑一遍拓扑排序即可

有环肯定无解,否则输出任意拓扑序即可

然后如果用倍增做的话,每个点往上跳2^i步认为是一个点,

2^i步的点连着2^(i-1)步的两个点,也是一棵入树、一棵出树,每棵树的点的总数是nlogn

点更多了,只是这样边更少,每个限制连接log个点

复杂度:树剖O((n+m)log^2),倍增O((n+m)log),

但是实践意义上看起来树剖跑得比较快…

懒得写代码了直接复制了

代码1(树链剖分,SSerxhs)

#include "bits/stdc++.h"
using namespace std;
template<typename T1,typename T2> istream &operator>>(istream &cin,pair<T1,T2> &a) { return cin>>a.first>>a.second; }
template<typename T1> istream &operator>>(istream &cin,vector<T1> &a) { for (auto &x:a) cin>>x; return cin; }
template<typename T1> istream &operator>>(istream &cin,valarray<T1> &a) { for (auto &x:a) cin>>x; return cin; }
template<typename T1,typename T2> ostream &operator<<(ostream &cout,const pair<T1,T2> &a) { return cout<<a.first<<' '<<a.second; }
template<typename T1,typename T2> ostream &operator<<(ostream &cout,const vector<pair<T1,T2>> &a) { for (auto &x:a) cout<<x<<'\n'; return cout; }
template<typename T1> ostream &operator<<(ostream &cout,const vector<T1> &a) { int n=a.size(); if (!n) return cout; cout<<a[0]; for (int i=1; i<n; i++) cout<<' '<<a[i]; return cout; }
template<typename T1,typename T2> bool cmin(T1 &x,const T2 &y) { if (y<x) { x=y; return 1; } return 0; }
template<typename T1,typename T2> bool cmax(T1 &x,const T2 &y) { if (x<y) { x=y; return 1; } return 0; }
template<typename T1> vector<T1> range(T1 l,T1 r,T1 step=1) { assert(step>0); int n=(r-l+step-1)/step,i; vector<T1> res(n); for (i=0; i<n; i++) res[i]=l+step*i; return res; }
template<typename T1> basic_string<T1> operator*(const basic_string<T1> &s,int m) { auto r=s; m*=s.size(); r.resize(m); for (int i=s.size(); i<m; i++) r[i]=r[i-s.size()]; return r; }
#if !defined(ONLINE_JUDGE)&&defined(LOCAL)
#include "my_header\debug.h"
#else
#define dbg(...) ;
#define dbgn(...) ;
#endif
typedef unsigned int ui;
typedef double db;
typedef long long ll;
#define all(x) (x).begin(),(x).end()
// template<typename T1,typename T2> void inc(T1 &x,const T2 &y) { if ((x+=y)>=p) x-=p; }
// template<typename T1,typename T2> void dec(T1 &x,const T2 &y) { if ((x+=p-y)>=p) x-=p; }
namespace HLD
{
	const int N=2e5+2;
	vector<int> e[N];
	int dfn[N],nfd[N],dep[N],f[N],siz[N],hc[N],top[N];
	int id;
	void dfs1(int u)
	{
		siz[u]=1;
		for (int v:e[u]) if (v!=f[u])
		{
			dep[v]=dep[f[v]=u]+1;
			dfs1(v);
			siz[u]+=siz[v];
			if (siz[v]>siz[hc[u]]) hc[u]=v;
		}
	}
	void dfs2(int u)
	{
		dfn[u]=++id;
		nfd[id]=u;
		if (hc[u])
		{
			top[hc[u]]=top[u];
			dfs2(hc[u]);
			for (int v:e[u]) if (v!=hc[u]&&v!=f[u]) dfs2(top[v]=v);
		}
	}
	int lca(int u,int v)
	{
		while (top[u]!=top[v])
		{
			if (dep[top[u]]<dep[top[v]]) swap(u,v);
			u=f[top[u]];
		}
		if (dep[u]>dep[v]) swap(u,v);
		return u;
	}
	int dis(int u,int v)
	{
		return dep[u]+dep[v]-(dep[lca(u,v)]<<1);
	}
	void init(int n)
	{
		for (int i=1; i<=n; i++)
		{
			e[i].clear();
			f[i]=hc[i]=0;
		}
		id=0;
	}
	void fun(int root)
	{
		dep[root]=1; dfs1(root); dfs2(top[root]=root);
	}
	vector<pair<int,int>> get_path(int u,int v)//u->v,注意可能出现 [r>l](表示反过来走)
	{
		//cerr<<"path from "<<u<<" to "<<v<<": ";
		vector<pair<int,int>> v1,v2;
		while (top[u]!=top[v])
		{
			if (dep[top[u]]>dep[top[v]]) v1.push_back({dfn[u],dfn[top[u]]}),u=f[top[u]];
			else v2.push_back({dfn[top[v]],dfn[v]}),v=f[top[v]];
		}
		v1.reserve(v1.size()+v2.size()+1);
		v1.push_back({dfn[u],dfn[v]});
		reverse(v2.begin(),v2.end());
		for (auto v:v2) v1.push_back(v);
		//for (auto [x,y]:v1) cerr<<"["<<x<<','<<y<<"] ";cerr<<endl;
		return v1;
	}
}
using HLD::lca,HLD::dis,HLD::dfn,HLD::nfd,HLD::dep,HLD::f,HLD::siz,HLD::get_path;
using HLD::fun,HLD::init;//5e5
const int N=2e5+5;
vector<int> e[N*7];
int in[N*4],out[N*4],deg[N*7],ans[N];
int id,z,y,ver;
void add(int u,int v)
{
	// cerr<<u<<' '<<v<<endl;
	e[u].push_back(v);
	++deg[v];
}
void build(int x,int l,int r)
{
	if (l==r)
	{
		in[x]=out[x]=l;
		return;
	}
	int c=x*2,m=l+r>>1;
	build(c,l,m); build(c+1,m+1,r);
	in[x]=++id; out[x]=++id;
	add(in[x],in[c]);
	add(in[x],in[c+1]);
	add(out[c],out[x]);
	add(out[c+1],out[x]);
}
void addin(int x,int l,int r)
{
	if (z<=l&&r<=y)
	{
		add(ver,in[x]);
		return;
	}
	int c=x*2,m=l+r>>1;
	if (z<=m) addin(c,l,m);
	if (y>m) addin(c+1,m+1,r);
}
void addout(int x,int l,int r)
{
	if (z<=l&&r<=y)
	{
		add(out[x],ver);
		return;
	}
	int c=x*2,m=l+r>>1;
	if (z<=m) addout(c,l,m);
	if (y>m) addout(c+1,m+1,r);
}
int main()
{
	ios::sync_with_stdio(0); cin.tie(0);
	cout<<fixed<<setprecision(15);
	int n,m,i,j;
	cin>>n>>m;
	id=n;
	init(n);
	for (i=1; i<n; i++)
	{
		int u,v;
		cin>>u>>v;
		HLD::e[u].push_back(v);
		HLD::e[v].push_back(u);
	}
	fun(1);
	build(1,1,n);
	while (m--)
	{
		int t,a,b,c;
		cin>>t>>a>>b>>c;
		if (dis(a,b)!=dis(b,c)+dis(a,c))
		{
			cout<<-1<<endl;
			return 0;
		}
		auto v=get_path(a,b);
		ver=dfn[c];
		if (t==1)
		{
			for (auto [l,r]:v)
			{
				if (l>r) swap(l,r);
				if (l<=ver&&ver<=r)
				{
					z=l; y=ver-1;
					if (z<=y) addin(1,1,n);
					z=ver+1; y=r;
					if (z<=y) addin(1,1,n);
					continue;
				}
				z=l; y=r;
				addin(1,1,n);
			}
		}
		else
		{
			for (auto [l,r]:v)
			{
				if (l>r) swap(l,r);
				if (l<=ver&&ver<=r)
				{
					z=l; y=ver-1;
					if (z<=y) addout(1,1,n);
					z=ver+1; y=r;
					if (z<=y) addout(1,1,n);
					continue;
				}
				z=l; y=r;
				addout(1,1,n);
			}
		}
	}
	queue<int> q;
	for (i=1; i<=id; i++) if (!deg[i]) q.push(i);
	id=0;
	while (q.size())
	{
		int u=q.front(); q.pop();
		if (u<=n) ans[nfd[u]]=++id;
		for (int v:e[u]) if (!--deg[v]) q.push(v);
	}
	if (id!=n)
	{
		cout<<-1<<endl;
		return 0;
	}
	for (i=1; i<=n; i++) cout<<ans[i]<<" \n"[i==n];
}

代码2(倍增,官方题解)

#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx,avx2,fma")
#pragma GCC target("sse4,popcnt,abm,mmx,tune=native")
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;

#define pb push_back
#define ff first
#define ss second

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pld;

const int INF = 1e9;
const ll LLINF = 1e18;
const int MOD = 1e9 + 7;

template<class K> using sset =  tree<K, null_type, less<K>, rb_tree_tag, tree_order_statistics_node_update>;

inline ll ceil0(ll a, ll b) {
    return a / b + ((a ^ b) > 0 && a % b);
}

void setIO() {
    ios_base::sync_with_stdio(0); cin.tie(0);
}

const int MAXN = 200'000;
const int LG = 18;
const int MAXM = 200'000;

vector<int> g[MAXN + 5];
int sz[MAXN + 5], in[MAXN + 5], par[MAXN + 5], depth[MAXN + 5], head[MAXN + 5], tim;
int n, m;

void dfs1(int x, int p){
    sz[x] = 1;
    for(int &i : g[x]){
        if(i == p) continue;
        dfs1(i, x);
        sz[x] += sz[i];
        if(g[x][0] == p || sz[i] > sz[g[x][0]]) swap(g[x][0], i);
    }
}
 
void dfs2(int x, int p){
    in[x] = tim++;
    par[x] = p;
    depth[x] = depth[p] + 1;
    for(int i : g[x]){
        if(i == p) continue;
        head[i] = (i == g[x][0] ? head[x] : i);
        dfs2(i, x);
    }
}

const int MAXSZ = MAXN + 2*MAXN*LG;
int down[LG][MAXN + 5];
int up[LG][MAXN + 5];
vector<int> dag[MAXSZ+ 5];
int lg[MAXN + 5];

void upd(int l, int r, int x, int t){
    if(l <= in[x] && in[x] <= r){
        if(l < in[x]) upd(l, in[x] - 1, x, t);
        if(in[x] < r) upd(in[x] + 1, r, x, t);
    } else {
        int sz = lg[r - l + 1];
        if(t == 2){
            dag[up[sz][l]].pb(x);
            dag[up[sz][r - (1 << sz) + 1]].pb(x);
        } else {
            dag[x].pb(down[sz][l]);
            dag[x].pb(down[sz][r - (1 << sz) + 1]);
        }
    }
}

//1 is down, 2 is up
void draw(int a, int b, int c, int t){
    while(head[a] != head[b]){
        if(depth[head[a]] > depth[head[b]]) swap(a, b);
        upd(in[head[b]], in[b], c, t);
        b = par[head[b]];
    }
    if(depth[a] > depth[b]) swap(a, b);
    upd(in[a], in[b], c, t);
}

bool vis[MAXSZ + 5], stk[MAXSZ + 5];
vector<int> ord;
bool fail;
int ind;

void dfs3(int x){
    if(fail) return;
    vis[x] = stk[x] = true;
    for(int i : dag[x]){
        if(i == x) continue;
        if(!vis[i]){
            dfs3(i);
        } else if(stk[i]){
            fail = true;
            break;
        }
    }
    stk[x] = false;
    if(x <= n) ord.pb(x);
}

int main(){
    setIO();
    cin >> n >> m;
    lg[1] = 0;
    for(int i = 2; i <= n; i++) lg[i] = lg[i/2] + 1;
    for(int i = 0; i < n - 1; i++){
        int a, b;
        cin >> a >> b;
        g[a].pb(b);
        g[b].pb(a);
    }
    tim = 0;
    dfs1(1, 1);
    head[1] = 1;
    dfs2(1, 1);
    for(int i = 1; i <= n; i++) down[0][in[i]] = up[0][in[i]] = i;
    ind = n + 1;
    for(int i = 1; i < LG; i++){
        for(int j = 0; j + (1 << i) <= n; j++){
            down[i][j] = ind++;
            up[i][j] = ind++;

            dag[down[i][j]].pb(down[i - 1][j]);
            dag[down[i][j]].pb(down[i - 1][j + (1 << (i - 1))]);

            dag[up[i - 1][j]].pb(up[i][j]);
            dag[up[i - 1][j + (1 << (i - 1))]].pb(up[i][j]);
        }
    }
    for(int i = 0; i < m; i++){
        int t, a, b, c;
        cin >> t >> a >> b >> c;
        draw(a, b, c, t);
    }
    fail = false;
    for(int i = 1; i <= n; i++){
        if(!vis[i]){
            dfs3(i);
            if(fail) break;
        }
    }
    if(fail){
        cout << -1 << endl;
        return 0;
    }
    reverse(ord.begin(), ord.end());
    int ans[n + 1];
    for(int i = 0; i < ord.size(); i++){
        ans[ord[i]] = i + 1;
    }
    for(int i = 1; i <= n; i++) cout << ans[i] << " ";
    cout << endl;
}

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值