8.10 数据结构+树论

P4197 Peaks

和永无乡相差不多()

先将高度离散化 将所有的边以边权为关键字排序

将所有的询问离线 以\(x\)为关键字排序 每一次查询加入一批权值小于等于\(x\)的边

为每一个连通块维护一个主席树 用并查集合并联通块 注意必须要把子节点合并到父节点上 且并查集必须初始化

每次查询的时候 我们查询这个连通块的根节点的主席树中第\(k\)大即可 注意是\(k\)

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
#define ls(p) t[p].son[0]
#define rs(p) t[p].son[1]
#define lson ls(p),l,mid
#define rson rs(p),mid+1,r
#define pii pair<int,int>
const int N = 5e5 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
// #define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m , q , now = 1 , ans[N] , rt[N] , a[N];

int fa[N];
int find ( int x ) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

vector<int> lsh;
struct edge { int u , v , w; } e[N];

struct que { int u , x , k , i; };
vector<que> qq;

struct Tree
{
	struct node { int son[2] , sz; } t[100000*32];
	int tot = 0;
	inl void up ( int p ) { t[p].sz = t[ls(p)].sz + t[rs(p)].sz; }
	void upd ( int &p , int l , int r , int x )
	{
		if ( !p ) p = ++tot; t[p].sz ++;
		if ( l == r ) return;
		if ( x <= mid ) upd ( lson , x );
		else upd ( rson , x );
	}
	void merge ( int &p , int l , int r , int u , int v )
	{
		if ( !u || !v ) return p = u + v , void();
		if ( l == r ) return t[p].sz = t[u].sz + t[v].sz , void();
		merge ( lson , ls(u) , ls(v) ) , merge ( rson , rs(u) , rs(v) ) , up(p);
	}
	int query ( int p , int l , int r , int k )
	{
		if ( t[p].sz < k ) return 0;
		if ( l == r ) return l;
		if ( t[rs(p)].sz >= k ) return query ( rson , k );
		else return query ( lson , k - t[rs(p)].sz );
	}
}T;
void merge ( int u , int v )
{
	int fu = find(u) , fv = find(v);
	if ( fu == fv ) return;
	fa[fu] = fv;
	T.merge ( rt[fv] , 1 , n , rt[fu] , rt[fv] );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , q = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , lsh.eb(a[i]) , fa[i] = i;
	sort ( lsh.begin() , lsh.end() );
	lsh.erase ( unique ( lsh.begin() , lsh.end() ) , lsh.end() );
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = lower_bound ( lsh.begin() , lsh.end() , a[i] ) - lsh.begin() + 1 , T.upd ( rt[i] , 1 , lsh.size() , a[i] ); 
	n = lsh.size();
	for ( int i = 1 ; i <= m ; i ++ ) e[i].u = read() , e[i].v = read() , e[i].w = read();
	sort ( e + 1 , e + m + 1 , [](const edge &a , const edge &b) { return a.w < b.w; } );
	for ( int i = 1 , u , x , k ; i <= q ; i ++ ) u = read() , x = read() , k = read() , qq.eb((que){u,x,k,i});
	sort ( qq.begin() , qq.end() , [](const que &a , const que &b) { return a.x < b.x; } );
	for ( auto [u,x,k,id] : qq )
	{
		while ( now <= m && e[now].w <= x ) merge ( e[now].u , e[now].v ) , now ++;
		int pos = T.query ( rt[find(u)] , 1 , n , k );
		if ( pos == 0 ) ans[id] = -1;
		else ans[id] = lsh[pos-1];
	}
	for ( int i = 1 ; i <= q ; i ++ ) cout << ans[i] << endl;
	return 0;	
}

P4981 父子

一个结论:一棵\(n\)个点的无根树 树的形态个数为\(n^{n-2}\)种 又因为树是有根的 所以我们需要再乘上一个\(n\)

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define int long long 
const int mod = 1e9 + 9;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
// #define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int ksm ( int base , int k )
{
	int res = 1;
	for ( ; k ; ( base *= base ) %= mod , k >>= 1 )
		if ( k & 1 ) ( res *= base ) %= mod;
	return res;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		int n = read();
		cout << ksm ( n , n - 1 ) << endl;
	}
	return 0;
}

P2680 [NOIP2015 提高组] 运输计划

第二次打()感觉第一遍没有掌握明白

\(LCA\)+树上差分+二分答案+点权转边权

考虑二分这个最短时间 考虑如何\(check()\)

我们预处理每一次询问的链的长度 那么这个长度如果大于\(mid\) 就将这条链在树上做一次差分标记 最后再\(dfs\)一次来统计

如果存在一条边 使得这条边经过所有的链 而且原来所有链长的最大值\(maxx\)减去这条边的长度 都小于等于\(mid\) 那么是合法的

这次用了更好理解的树中\(dfs\)写法 不过常数大了三倍不止()

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define int long long 
const int mod = 1e9 + 9;
const int N = 3e5 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
// #define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m , lca[N] , u[N] , v[N] , d[N] , maxx , pass[N];

int fa[N] , sz[N] , dep[N] , son[N] , dis[N] , a[N];
int timer , rev[N] , pos[N] , top[N];

vector<pii> e[N];
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }

struct LCA 
{
	void dfs1 ( int u , int f )
	{
		fa[u] = f , dep[u] = dep[f] + 1 , sz[u] = 1;
		for ( auto [v,w] : e[u] ) 
			if ( v ^ f )
			{
				a[v] = w , dis[v] = dis[u] + w;
				dfs1 ( v , u );
				sz[u] += sz[v];
				if ( sz[son[u]] < sz[v] ) son[u] = v;
			}
	}

	void dfs2 ( int u , int tp )
	{
		top[u] = tp , pos[u] = ++timer , rev[timer] = u;
		if ( son[u] ) dfs2 ( son[u] , tp );
		for ( auto [v,w] : e[u] ) if ( v ^ fa[u] && v ^ son[u] ) dfs2 ( v , v );
	}
	int lca ( int u , int v )
	{
		while ( top[u] != top[v] )
		{
			if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
			u = fa[top[u]];
		}
		if ( dep[u] < dep[v] ) swap ( u , v );
		return v;
	}
}L;

void dfs ( int u , int f ) { for ( auto [v,w] : e[u] ) if ( v ^ f ) dfs ( v , u ) , pass[u] += pass[v]; }

int check ( int x )
{
	int tot = 0;
	for ( int i = 1 ; i <= n ; i ++ ) pass[i] = 0;
	for ( int i = 1 ; i <= m ; i ++ ) if ( x < d[i] ) tot ++ , pass[u[i]] ++ , pass[v[i]] ++ , pass[lca[i]] -= 2;
	dfs ( 1 , 0 );
	for ( int i = 1 ; i <= n ; i ++ ) if ( maxx - a[i] <= x && pass[i] == tot ) return 1;
	return 0;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 , u , v , w ; i < n ; i ++ ) u = read() , v = read() , w = read() , add ( u , v , w ) , add ( v , u , w );
	L.dfs1 ( 1 , 0 ) , L.dfs2 ( 1 , 1 );
	for ( int i = 1 ; i <= m ; i ++ )
	{
		u[i] = read() , v[i] = read() , lca[i] = L.lca ( u[i] , v[i] );
		d[i] = dis[u[i]] + dis[v[i]] - 2 * dis[lca[i]];
		maxx = max ( maxx , d[i] );
	}
	int l = 0 , r = maxx;
	while ( l <= r )
	{
		if ( check(mid) ) r = mid - 1;
		else l = mid + 1;
	}
	cout << l << endl;
	return 0;
}

P3459 [POI2007] MEG-Megalopolis

\(dfn\)序+线段树

我们先对于每一个点进行\(dfs\) 为他们赋一个\(dfn\)

之后按照\(dfn\)序建树 树中节点初始值赋为初始深度\(dep\)

对于每一次修路\((u,v)\) 影响的值就是\(v\)子树内到根节点的路径需要减一 又因为\(dfn\)序是连续的 那么直接线段树区间修改即可

查询的时候直接单点查询\(dfn[u]\)即可

错误:查询的时候查了\(u\)而不是\(dfn[u]\)

(实际上这道题显然也可以树剖 代码见树剖练习题)

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
const int N = 5e5 + 5;
// char buf[1<<24] , *p1 , *p2;
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m , dep[N] , rev[N] , sz[N];

int dfn[N] , timer;

vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }

void dfs ( int u , int f )
{
	dfn[u] = ++timer , rev[timer] = u , dep[u] = dep[f] + 1 , sz[u] = 1;
	for ( auto v : e[u] ) if ( v ^ f ) dfs ( v , u ) , sz[u] += sz[v];
}

struct segtree
{
	struct node { int sum , add; } t[N<<2];
	inl void up ( int p ) { t[p].sum = t[ls].sum + t[rs].sum; }
	inl void pushadd ( int p , int l , int r , int val ) { t[p].sum += ( r - l + 1 ) * val , t[p].add += val; }
	inl void down ( int p , int l , int r ) { if ( t[p].add ) pushadd ( lson , t[p].add ) , pushadd ( rson , t[p].add ) , t[p].add = 0; }
	void build ( int p , int l , int r )
	{
		if ( l == r ) return t[p].sum = dep[rev[l]] , void();
		build ( lson ) , build ( rson ) , up(p);
	}
	void upd ( int p , int l , int r , int x , int y , int val )
	{
		if ( x <= l && r <= y ) return pushadd ( p , l , r , val ) , void();
		down(p,l,r);
		if ( x <= mid ) upd ( lson , x , y , val );
		if ( mid + 1 <= y ) upd ( rson , x , y , val );
		up(p);
	}
	int query ( int p , int l , int r , int x )
	{
		if ( l == r ) return t[p].sum;
		down(p,l,r);
		if ( x <= mid ) return query ( lson , x );
		else return query ( rson , x );
	}
}T;

char ch;
signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
	m = read();
	dep[0] = -1 , dfs ( 1 , 0 ) , T.build ( 1 , 1 , n );
	for ( int i = 1 , u , v ; i <= n + m - 1 ; i ++ )
	{
		cin >> ch;
		if ( ch == 'A' ) { u = read() , v = read() , T.upd ( 1 , 1 , n , dfn[v] , dfn[v] + sz[v] - 1 , -1 ); }
		else u = read() , cout << T.query ( 1 , 1 , n , dfn[u] ) << endl;//dfn[u]不要写成u了!
	}
	return 0;
}
posted @ 2023-08-10 19:55  Echo_Long  阅读(5)  评论(0编辑  收藏  举报