集训总结 Day1 杂题选讲

7.4  Thu.

杂题选讲

出处:

牛客

CCPC 东北赛区

CCPC 长春赛区

A.

在这里插入图片描述
依次考虑每个人

起点到终点构成一个矩形,矩形内有关键点的话答案就是 两点间 d i s dis dis

基于曼哈顿距离的特殊性,将整张图分成九个部分,每个部分内的问题是相似的
在这里插入图片描述
第一想法是对每个矩形内部维护相应的关键点的 值 ( 比如 x + y , x − y x+y,x-y x+y,xy 等 ) 的 M a x / M i n Max / Min Max/Min

可能需要上高级数据结构

注意到 x , y x,y x,y 只有 1000 1000 1000 ,那么仅枚举 9 9 9 个部分在带上 l o g log log 查询的复杂度显得有点多余了

更好的做法是枚举每一列,实际上每一列有用的点最多只有三个(离矩形上下边界最近的)

这个东西可以直接 O ( V 2 ) O(V^2) O(V2) 在原地图上递推得到

int mp[M][M] , up[M][M] , dn[M][M] ; // 每个点上/下第一个关键点
void pre_work()
{
    for(int j = 1 ; j <= 1000 ; j ++ ) {
        up[0][j] = -1e9 ;
        for(int i = 1 ; i <= 1000 ; i ++ ) {
            if( mp[i][j] ) up[i][j] = i ;
            else up[i][j] = up[i-1][j] ;
        }
        dn[1001][j] = 1e9 ;
        for(int i = 1000 ; i >= 1 ; i -- ) {
            if( mp[i][j] ) dn[i][j] = i ;
            else dn[i][j] = dn[i+1][j] ;
        }
    }
}
void solve( int sx , int sy , int tx , int ty ) // 从左到右 
{
    int mn = min( sx , tx ) , mx = max( sx , tx ) ;
    int ans = abs(sx-tx) + abs(sy-ty) , ext = 1e9 ;
    for(int j = 1 ; j <= 1000 ; j ++ ) {
        if( j < sy ) {
            ext = min( ext , 2*(sy-j+mn-up[mn][j]) ) ;
            ext = min( ext , 2*(sy-j+dn[mx][j]-mx) ) ;
            if( dn[mn][j] <= mx ) {
                ext = min( ext , 2*(sy-j) ) ;
            }
        }
        else if( j <= ty ) {
            ext = min( ext , 2*(mn-up[mn][j]) ) ;
            ext = min( ext , 2*(dn[mx][j]-mx) ) ; 
            if( dn[mn][j] <= mx ) {
                ext = 0 ;
                break ;
            }
        }
        else {
            ext = min( ext , 2*(j-ty+mn-up[mn][j]) ) ;
            ext = min( ext , 2*(j-ty+dn[mx][j]-mx) ) ;
            if( dn[mn][j] <= mx ) {
                ext = min( ext , 2*(j-ty) ) ;
            }
        }
    }
    printf("%d\n" , ext+ans ) ;
}

*B.

好题
在这里插入图片描述
线段树好像很难处理这种东西(带修主席树?),第一想法是分块

发现问题在于 合并两个整块时 颜色信息 无法快速维护,这个信息不具有 “可加性”

没办法了,考虑莫队,动态维护询问区间,让左右端点 + 1 , − 1 +1 ,-1 +1,1

还有一个出发点是 这个题给了 询问区间不交 的性质,莫队的指针移动就是 O ( n ) O(n) O(n)

莫队的劣势在于 本题带修,需要考虑操作顺序的问题,解决办法是 动态维护时间戳

发现对于当前维护的区间来说,每种颜色可以 只用时间戳最小的来代表

维护 n n n s e t set set 表示 n n n 种颜色的时间戳集合 ,每个 s e t set set b e g i n begin begin 加到树状数组里,查询时只需查时间戳前缀的颜色数

左右端点移动时 先删掉对应 s e t set set b e g i n begin begin 在树状数组中的贡献,修改完 s e t set set 后再补上新 b e g i n begin begin 的贡献

int n , m ;
vector<int> L[N] , R[N] ;
int l1 , l2 ;
struct nn
{
	int l , r , c , tim ;
}op[N] ;
struct qe
{
	int l , r , tim , id ;
}q[N] ;
bool cmp ( qe x , qe y )
{
	return x.l < y.l ;
}
int lx , rx , ans[N] ;
set<int> s[N] ;
struct BIT
{
	int t[N] ;
	inline int lowbit( int x ) { return x&-x ; }
	void add( int p , int x ) { for( ; p <= n ; p += lowbit(p) ) t[p] += x ; }
	int ask( int p ) {
		int res = 0 ;
		for( ; p ; p -= lowbit(p) ) res += t[p] ;
		return res ;
	}
}t ;

void Insert( int c , int tim ) // 添加 x 操作 
{
	if( !s[c].empty() ) {
		int tm = *s[c].begin() ;
		t.add( tm , -1 ) ;
	}
	s[c].insert( tim ) ;
	int tm = *s[c].begin() ;
	t.add( tm , 1 ) ; 
}
void Erase( int c , int tim )
{
	int tm = *s[c].begin() ;
	t.add( tm , -1 ) ;
	
	s[c].erase( tim ) ;
	if( !s[c].empty() ) {
		int tm = *s[c].begin() ;
		t.add( tm , 1 ) ; 
	}
}

int main()
{
	scanf("%d%d" , &n , &m ) ;
	int opt ;
	for(int i = 1 ; i <= m ; i ++ ) {
		scanf("%d" , &opt ) ;
		if( opt == 1 ) {
			l1 ++ ;
			scanf("%d%d%d" , &op[l1].l , &op[l1].r , &op[l1].c ) ;
			op[l1].tim = i ;
			L[op[l1].l].push_back( l1 ) ;
			R[op[l1].r].push_back( l1 ) ;
		}
		else {
			l2 ++ ;
			scanf("%d%d" , &q[l2].l , &q[l2].r ) ;
			q[l2].tim = i ;
			q[l2].id = l2 ;
		}
	}
	sort( q+1 , q+l2+1 , cmp ) ;
}

复杂度是 O ( n l o g ) O(nlog) O(nlog)

C.

在这里插入图片描述
1 0 4 10^4 104 个点放到 50 50 50 条直线上?

直接随机化,每次随机找两个点求出斜率作为 K K K c h e c k check check

c h e c k check check 过程:

对于每个点,找到经过它斜率为 K K K 的直线截距 B B B,只需统计 B B B 的种类即可

需要注意 B B B d o u b l e double double 的话 m a p map map 是没法有效去重的,要对等式变形,去分母搞成整数做

D.

在这里插入图片描述
式子很奇怪,进行一步常用变形:

{ a i − i ≤ a j − j i − b i ≤ j − b j      ⟹       { X i ≤ X j Y i ≤ Y j \begin{cases} a_i-i\leq a_j-j\\ i-b_i\leq j-b_j \end{cases} \ \ \ \ \Longrightarrow\ \ \ \ \ \begin{cases} X_i\leq X_j\\ Y_i\leq Y_j \end{cases} {aiiajjibijbj         {XiXjYiYj

其实是一个二维偏序

首先按 X X X 排序,维护一个单减栈:

一个 Y i Y_i Yi 可以连通其左下部分的所有点,每加入一个 Y i Y_i Yi 可以把栈内所有小于它的点连通,这些点先都出栈

那么对于已加入的点来说, Y i Y_i Yi 显然越小越好(越容易被后面连上)

那么一个连通块就只用其 Y i Y_i Yi 最小的那个来代表,把出栈的点中最小的塞回去,就 ok 了

int n ;
struct nn
{
	int x , y ;
}a[N] ;
bool cmp ( nn x , nn y )
{
	return x.x < y.x || (x.x==y.x&&x.y<y.y) ;
}
int st[N] , top ;

int main()
{
	scanf("%d" , &n ) ;
	int b , c ;
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d%d" , &b , &c ) ; 
		a[i].x = b-i ;
		a[i].y = i-c ;
	}
	sort( a+1 , a+n+1 , cmp ) ;
	for(int i = 1 ; i <= n ; i ++ ) {
		int Min = a[i].y ;
		while( top && a[i].y >= st[top] ) {
			Min = min( Min , st[top] ) ;
			top -- ;
		}
		st[++top] = Min ;
	}
	printf("%d" , top ) ;
	return 0 ;
}

巧妙的单调栈用法

E.

在这里插入图片描述
树形 DP ,发现儿子子树 s i z e size size 的奇偶性会影响走出来时的状态,按这个分讨一下

*F.

好题
在这里插入图片描述
看到最大距离最小先考虑二分答案

二分了当前最大距离 m i d mid mid ,考虑怎么 c h e c k check check

整体思路是 枚举每个点对,标记出能作为根的节点,最后对所有标记取交

首先 对于点对 ( x , y ) (x,y) (x,y) ,怎么找能作为根的节点

一个比较显然的事情:两个点的 LCA 不管怎么选根,一定在它们之间的路径上

继续拆:能让 x x x 合法的根 与 能让 y y y 合法的根,分别标记再取交

不妨考虑 x x x ,手玩一下就知道怎么做了

在这里插入图片描述
这里第二种情况是用了一个 全局增加 + 补集减少 的 trick,因为补集是一棵完整子树,dfs 序上差分即可

int n , m ;
vector<int> E[N] ;
int dep[N] , fat[N][22] , dfn[N] , tim , ed[N] ;
void dfs( int x , int fa )
{
	dfn[x] = ++tim ;
	dep[x] = dep[fa] + 1 ; fat[x][0] = fa ;
	for(int i = 1 ; i <= 20 ; i ++ ) fat[x][i] = fat[fat[x][i-1]][i-1] ;
	for(int t : E[x] ) {
		if( t == fa ) continue ;
		dfs( t , x ) ;
	}
	ed[x] = tim ;
}
int LCA( int x , int y )
{
	if( dep[x] < dep[y] ) swap( x , y ) ;
	for(int i = 20 ; i >= 0 ; i -- ) {
		if( dep[fat[x][i]] >= dep[y] ) x = fat[x][i] ;
	}
	if( x == y ) return x ;
	for(int i = 20 ; i >= 0 ; i -- ) {
		if( fat[x][i] != fat[y][i] ) x = fat[x][i] , y = fat[y][i] ;
	}
	return fat[x][0] ;
}
int Get( int x , int p )
{
	for(int i = 20 ; i >= 0 ; i -- ) {
		if( (p>>i)&1 ) {
			x = fat[x][i] ;
		}
	}
	return x ;
}
int X[N] , Y[N] , ADD , c[N] ;
inline void add( int l , int r , int d )
{
	c[l] += d , c[r+1] -= d ;
}
bool check( int mid )
{
	ADD = 0 ;
	for(int i = 0 ; i <= n+1 ; i ++ ) c[i] = 0 ;
	
	for(int i = 1 ; i <= m ; i ++ ) {
		int x = X[i] , y = Y[i] , lca = LCA( x , y ) ;
		int dis = dep[x]+dep[y]-2*dep[lca] ;
		if( dis <= mid ) {
			ADD += 2 ;
			continue ;
		}
		if( dep[x] - dep[lca] > mid ) {
			int p = Get( x , mid ) ;
			add( dfn[p] , ed[p] , 1 ) ;
		}
		else {
			ADD ++ ;
			int p = Get( y , dis-mid-1 ) ;
			add( dfn[p] , ed[p] , -1 ) ;
		}
		
		if( dep[y] - dep[lca] > mid ) {
			int p = Get( y , mid ) ;
			add( dfn[p] , ed[p] , 1 ) ;
		}
		else {
			ADD ++ ;
			int p = Get( x , dis-mid-1 ) ;
			add( dfn[p] , ed[p] , -1 ) ;
		}
	}
	for(int i = 1 ; i <= n ; i ++ ) {
		c[i] += c[i-1] ;
		if( c[i]+ADD == 2*m ) {
			return 1 ;
		}
	}
	return 0 ;
}

*G.

在这里插入图片描述
讨厌的数数题

对于最终一个合法的序列,考虑一下怎么去找子问题

结合那个性质,当前末尾位置往前 K K K 个必定是一个排列,以此作为状态

转移就往前枚举一个位置 j j j 作为上一个排列的结尾,为避免重复,钦定 j j j 是最靠右的能构成排列的位置,也就是说, [ j + 1 , i − 1 ] [j+1,i-1] [j+1,i1] 任意一个位置作为结尾都不能是 1 ∼ K 1\sim K 1K 的排列

( 这也是计数 dp 的核心 )

为了要跟 j j j 前面构成排列,此时 [ j + 1 , i ] [j+1,i] [j+1,i] 要填哪些数字是固定的,只是方案不定,那么:

f [ i ] ← f [ j ] × ( i − j ) ! f[i]\leftarrow f[j]\times(i-j)! f[i]f[j]×(ij)!

这样我们就会发现答案大了!

为什么呢?注意我们上面钦定的那一条

如果单纯 ( i − j ) ! (i-j)! (ij)! 的话可能会使 [ j + 1 , i − 1 ] [j+1,i-1] [j+1,i1] 出现新的能构成排列的位置

设这个系数为 g [ i ] g[i] g[i] ,表示在 1 ∼ K 1\sim K 1K 的排列后接上 i i i 个数, i i i 能作为右端点构成排列,且 [ K + 1 , K + i − 1 ] [K+1,K+i-1] [K+1,K+i1] 作为右端点都 不能 构成 排列,填这 i i i 个数的方案

首先 g [ i ] = i ! g[i]=i! g[i]=i!

接下来考虑容斥:

j + 1 j+1 j+1 开始枚举到 i − 1 i-1 i1钦定当前枚举的 x x x 是从左往右第一个能构成排列的,也就是 [ j + 1 , x − 1 ] [j+1,x-1] [j+1,x1] 任意一个位置都不能构成排列

发现问题递归了,这就是 g [ x − j ] g[x-j] g[xj] ,后面 [ x + 1 , i ] [x+1,i] [x+1,i] 就随便填了,那么 g g g 就可以求, f f f 也就能求了

在这里插入图片描述

int n , K ;
LL f[N] , fac[1010] , g[1010] ;// 前 i 位置,钦定 i 包含在 以i结尾的 1~k 的排列中,方案数 

int main()
{
	scanf("%d%d" , &n , &K ) ;
	fac[0] = 1 ;
	for(int i = 1 ; i <= K ; i ++ ) fac[i] = fac[i-1] * i % mod ;
	f[K] = fac[K] ;
	for(int i = 1 ; i <= K ; i ++ ) {
		g[i] = fac[i] ;
		for(int j = 1 ; j < i ; j ++ ) g[i] = ( g[i] - fac[j]*g[i-j]%mod + mod ) % mod ;
	}
	for(int i = K+1 ; i <= n ; i ++ ) {
		// 用 从i往前找到的第一个 p 使得 [p-k+1,p] 为 1~k排列 来划分 
		for(int j = 1 ; j <= K ; j ++ ) {
			f[i] = ( f[i] + f[i-j] * g[j] % mod ) % mod ; // 为避免重复,需要处理一个系数数组 g 
		}
	}
	printf("%lld" , f[n] ) ;
	return 0 ;
}

H.

在这里插入图片描述
非常神奇的一道题

首先按 b i b_i bi 分层,每层内可以按 L L L 排序

那么题目中的限制转化为:

同一层内不能有相互包含的 , 相邻层 之间不能下层不能包含上层

这里这考虑相邻层即可,假设 i i i j    ( b i + 1 < b j ) j\ \ (b_i+1<b_j) j  (bi+1<bj) 包含,由于 b j − 1 b_j-1 bj1 层中必有一个包含 j j j ,同理往上推,会得到 b i + 1 b_i+1 bi+1 层中必有一个包含 i i i

那么就只需要保证相邻层之间 下层每个都被上层某个包含,同一层内 随 L L L 单增, R R R 单增(若同层有 L L L 相同,显然不合法了)

越靠上的层显然越大越好,那么就是贪心的尽可能把右端点往右放
在这里插入图片描述
注意每层右端点单增的限制,所以要从大到小枚举 L L L ,维护当前最小右端点 n o w now now,找到一个新的右端点时要与 n o w − 1 now-1 now1 M i n Min Min

int n ;
vector<int> ve[M] ;
int l[N] , r[N] ;
bool cmp( int x , int y )
{
	return l[x] < l[y] ;
}

int main()
{
	scanf("%d" , &n ) ;
	int b , Max = 0 ;
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d%d" , &l[i] , &b ) ;
		Max = max( Max , b ) ;
		ve[b].push_back( i ) ;
	} // 对每一层分开做
	for(int i = 0 ; i <= Max ; i ++ ) {
		if( ve[i].size() == 0 ) {
			printf("-1\n") ;
			return 0 ;
		}// 断档 
		sort( ve[i].begin() , ve[i].end() , cmp ) ;
		if( i == 0 ) {
			int now = 1e6 ;
			for(int j = ve[i].size()-1 ; j >= 0 ; j -- ) {
				int id = ve[i][j] ;
				if( j && l[id] == l[ve[i][j-1]] ) {
					printf("-1\n") ;
					return 0 ;
				}
				r[id] = now ;
				now -- ;
			}
		} 
		else {
			int now = 1e6+1 ;
			for(int j = ve[i].size()-1 , p = ve[i-1].size()-1 ; j >= 0 ; j -- ) {
				if( j && l[ve[i][j]] == l[ve[i][j-1]] ) {
					printf("-1\n") ;
					return 0 ;
				} // 同层有相同 L
				while( p-1 >= 0 && l[ve[i-1][p]] > l[ve[i][j]] ) {
					p -- ;
				}// 找到最接近的上一层的左端点
				if( l[ve[i-1][p]] <= l[ve[i][j]] ) {
					if( l[ve[i-1][p]] < l[ve[i][j]] ) {
						r[ve[i][j]] = min( now-1 , r[ve[i-1][p]] ) ;
						now = r[ve[i][j]] ;
					}
					else {
						r[ve[i][j]] = min( now-1 , r[ve[i-1][p]]-1 ) ;
						now = r[ve[i][j]] ;
					}
				}
				else {
					printf("-1\n") ;
					return 0 ;
				}
			}
		}
	}
	for(int i = 1 ; i <= n ; i ++ ) {
		if( l[i] > r[i] ) {
			printf("-1\n") ;
			return 0 ;
		}
	}
	for(int i = 1 ; i <= n ; i ++ ) {
		printf("%d\n" , r[i] ) ;
	}
	return 0 ;
}

I.

在这里插入图片描述
经过反复手玩,会发现

1 1 1 操作的相对顺序是固定的,且只针对最终序列中形如 ( ) () () 的,只能从左往右放

对于 2 2 2 操作,实际上唯一的限制只有 必须在某个 1 1 1 操作之后进行

那么倒着做,每个 ( ) () () 相当于进行了一次 将新的 2 2 2 操作生成的括号 插入到原序列中,再把这个 ( ) () () 对应的 1 1 1 操作放到开头,那么这就是一个常规的组合问题

由于 “选择不同区间视为不同 2 2 2 操作”,在插入一段 2 2 2 操作时会有顺序问题,需要乘上排列

int main()
{
	scanf("%s" , ch+1 ) ;
	n = strlen( ch+1 ) ;
	maxn = n ;
	pre_work() ;
	for(int i = 1 ; i < n ; i ++ ) {
		if( ch[i] == '(' && ch[i+1] == ')' ) {
			cnt ++ ;
			i ++ ;
			while( i+1 <= n && ch[i+1] == ')' ) v[cnt] ++ , i ++ ;
		}
	}
	LL ans = 1 ;
	int S = 0 ;
	for(int i = cnt ; i >= 1 ; i -- ) {
		S += v[i] ;
		ans = ans * C(S,v[i]) % mod * fac[v[i]] % mod ;
		S ++ ;
	}
	printf("%lld" , ans ) ;
	return 0 ;
}

*J

好题

在这里插入图片描述
每次询问只与一部分数有关,考虑对这些数的出现次数进行根号分治

1.若 c n t p < n cnt_p<\sqrt n cntp<n c n t q < n cnt_q<\sqrt n cntq<n

   直接暴力双指针即可,复杂度 O ( n ) O(\sqrt n) O(n )

2.若 其中一个出现次数大于等于 n \sqrt n n ,不妨设为 p p p

   这样的 p p p 只有 n \sqrt n n 个,称这些为 关键点

   只有两种数的逆序对是好算的,等价于 对于每一个 q ∈ [ l , r ] q \in [l,r] q[l,r] 后面的 p ∈ [ l , r ] p\in [l,r] p[l,r] 出现次数 求和

   显然等价于预处理每一个 q q q 后面 p p p 的出现次数,再滚前缀和,然后把 r r r 后面的减掉即可

   由于 p p p 只有 n \sqrt n n 个,考虑开数组 t [ p ] [ i ] t[p][i] t[p][i] 表示 对于 关键点 p p p i i i 位置对应的颜色中,出现 p p p 次数 到 i i i 的后缀和

本题卡空间,块长需要微调

#include<bits/stdc++.h>
using namespace std ;

typedef long long LL ;
const int N = 1e5 + 5 , B = 155 ; 

int n , T , a[N] , cnt[N] , lst[N] , bl ;
int len , c[B] ;
LL s[B][N] , t[B][N] ; // 只维护 B种,对同颜色滚前缀和 
vector<int> ps[N] ;
void sol1( int l , int r , int p , int q ) // sqrt n 双指针 
{
	int j = -1 , ans = 0 ;
	while( j+1 < ps[q].size() && ps[q][j+1] < l ) j ++ ;
	int st = j ;
	for(int i = 0 ; i < ps[p].size() ; i ++ ) {
		if( ps[p][i] < l ) continue ;
		if( ps[p][i] > r ) break ;
		while( j+1 < ps[q].size() && ps[q][j+1] <= ps[p][i] ) {
			j ++ ;
		}
		ans += j-st ;
	}
	printf("%d\n" , ans ) ;
}
int nam[N] ;
void sol2( int l , int r , int p , int q )
{
	if( cnt[p] == 0 || cnt[q] == 0 ) {
		printf("0\n") ;
		return ;
	}
	if( cnt[p] >= bl ) {
		int ID = nam[p] ;
		int p1 = lower_bound( ps[q].begin() , ps[q].end() , l ) - ps[q].begin() ;
		int p2 = upper_bound( ps[q].begin() , ps[q].end() , r ) - ps[q].begin() ;
		int p3 = lower_bound( ps[p].begin() , ps[p].end() , l ) - ps[p].begin() ;
		p1 -- , p2 -- ;
		int L = ps[q][p1] , R = ps[q][p2] ;
		LL ans = s[ID][R] - s[ID][L] - 1LL*(p2-p1)*(p3-1) ;
		printf("%lld\n"  , ans ) ;
		return ;
	}
	int ID = nam[q] ;
	int p1 = lower_bound( ps[p].begin() , ps[p].end() , l ) - ps[p].begin() ;
	int p2 = upper_bound( ps[p].begin() , ps[p].end() , r ) - ps[p].begin() ;
	int p3 = upper_bound( ps[q].begin() , ps[q].end() , r ) - ps[q].begin() ; 
	int L = ps[p][p1] , R = ps[p][p2] ;
	LL ans = t[ID][L] - t[ID][R] - 1LL*(p2-p1)*(ps[q].size()-p3-1) ;
	printf("%lld\n" , ans ) ;
	return ;
}

int main()
{
	scanf("%d%d" , &n , &T ) ;
	bl = max( n/151 , int(sqrt(n)) ) ;
	for(int i = 1 ; i <= n ; i ++ ) ps[i].push_back( 0 ) ; // 占位 
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d" , &a[i] ) ;
		ps[a[i]].push_back( i ) ;
		cnt[a[i]] ++ ;
		if( cnt[a[i]] == bl ) c[++len] = a[i] , nam[a[i]] = len ;
	}
	for(int i = 1 ; i <= n ; i ++ ) ps[i].push_back( n+1 ) ;// 占位 
	for(int i = 1 ; i <= len ; i ++ ) {
		int T = 0 ;
		memset( lst , 0 , sizeof lst ) ;
		for(int j = 1 ; j <= n ; j ++ ) {
			if( a[j] == c[i] ) T ++ ;
			s[i][j] = s[i][lst[a[j]]] + T ;
			lst[a[j]] = j ;
		}
		T = 0 ;
		memset( lst , 0 , sizeof lst ) ;
		for(int j = n ; j >= 1 ; j -- ) {
			if( a[j] == c[i] ) T ++ ;
			t[i][j] = t[i][lst[a[j]]] + T ;
			lst[a[j]] = j ;
		}
	}
	int l , r , p , q ;
	for(int i = 1 ; i <= T ; i ++ ) {
		scanf("%d%d%d%d" , &l , &r , &p , &q ) ;
		if( cnt[p] < bl && cnt[q] < bl ) {
			sol1( l , r , p , q ) ;
		}
		else {
			swap( p , q ) ;
			sol2( l , r , p , q ) ;
		}
	}
	return 0 ;
}
  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值