题目
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代码、官方题解代码
线段树优化建图
题解
感觉很典,就是三个板子拼到了一起的这么一个题
有大小关系限制,考虑拓扑排序,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;
}

文章介绍了如何使用线段树优化建图的方法解决含有大小关系限制的路径问题,通过树链剖分和倍增策略,结合拓扑排序,有效地构建满足限制条件的路径排列,时间复杂度分别为O((n+m)log^2)和O((n+m)log)。
1590

被折叠的 条评论
为什么被折叠?



