传送门:【codeforces】Codeforces Round #310 (Div. 1)
好久没写题解了,今天来补个好了……
这一场div1大概是我见到过的对我来说最容易的一场了……除了A题意略坑,C纠结了一会,其他题都是一眼秒……
A. Case of Matryoshkas
题意:一共有N个套娃,现在放成M堆,每一堆满足小的嵌套在大的里面,有个限制就是,一个套娃不能同时嵌套两个套娃。
然后每秒你能执行下列两个操作中的其中一个:
1.选择两个套娃A、B,将A放在B里面,需要满足B是单独一个的,即B里面没有套娃,且B不在别的套娃里面,A则需要满足不在别的套娃里面。
2.如果A被B直接套住,且B没被其他套娃套住,那么你能将套娃A从B里面取出。
问最少多少秒,你能将所有套娃合并成一堆,即1->2->3->……->n。
分析:根据题意,注意到只有和1连续的套娃,是可以不用拆分的,其他都是要完全拆除的,所以根据这个直接模拟就行了。
坑点:坑点只有未修改之前的题面了,之前表述我感觉不太清楚(导致我wa三发,每发都是不同的题意……),修改后题面就很清楚了……
my code:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )
const int MAXN = 100005 ;
const int mod = 1e9 + 7 ;
int n , m ;
int a[MAXN] ;
void solve () {
int ans = 0 , cnt = 0 ;
for ( int i = 1 ; i <= m ; ++ i ) {
int t , x ;
scanf ( "%d" , &t ) ;
for ( int j = 1 ; j <= t ; ++ j ) {
scanf ( "%d" , &a[j] ) ;
}
if ( a[1] == 1 ) {
int flag = 0 ;
for ( int j = 2 ; j <= t ; ++ j ) {
if ( a[j] - a[j - 1] != 1 ) {
ans += t - j + 1 ;
cnt += t - j + 2 ;
flag = 1 ;
break ;
}
}
if ( !flag ) ++ cnt ;
} else ans += t - 1 , cnt += t ;
}
printf ( "%d\n" , ans + cnt - 1 ) ;
}
int main () {
while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
return 0 ;
}
分析:考虑相邻两个陆地(l1,r1),(l2,r2),那么我们可以选用的桥的长度区间为[l2-r1,r2-l1],对于所有的n-1个这样的区间,我们该如何分配桥呢?
考虑两个得到的区间[x1,y1],[x2,y2]:
1.区间交叉,那么我们优先给y小的区间分配长度小的线段。
2.区间覆盖,那么我们优先给被覆盖的区间分配长度小的线段。
(YY一下,正确性很显然)
根据以上两条规则,我们只需要将得到的区间排序,先按右端点从小到大排序,然后右端点相同按照左端点从大到小排序。然后将桥插入到set,依次遍历区间,选取在这个区间内的长度最小的桥即可。
my code:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )
const int MAXN = 200005 ;
const int mod = 1e9 + 7 ;
struct Node {
LL l , r ;
int idx ;
bool operator < ( const Node& a ) const {
if ( r !=a.r ) return r < a.r ;
return l > a.l ;
}
} ;
Node a[MAXN] ;
LL L[MAXN] , R[MAXN] ;
set < pair < LL , int > > s ;
set < pair < LL , int > > :: iterator it ;
LL ans[MAXN] ;
int n , m ;
void solve () {
LL x ;
s.clear () ;
for ( int i = 1 ; i <= n ; ++ i ) {
scanf ( "%I64d%I64d" , &L[i] , &R[i] ) ;
if ( i > 1 ) {
a[i - 1].l = L[i] - R[i - 1] ;
a[i - 1].r = R[i] - L[i - 1] ;
a[i - 1].idx = i - 1 ;
}
}
for ( int i = 1 ; i <= m ; ++ i ) {
scanf ( "%I64d" , &x ) ;
s.insert ( make_pair ( x , i ) ) ;
}
sort ( a + 1 , a + n ) ;
-- n ;
for ( int i = 1 ; i <= n ; ++ i ) {
if ( s.begin () == s.end () ) {
printf ( "No\n" ) ;
return ;
}
it = s.lower_bound ( make_pair ( a[i].l , 0 ) ) ;
if ( it == s.end () || it->first > a[i].r ) {
printf ( "No\n" ) ;
return ;
}
ans[a[i].idx] = it->second ;
s.erase ( it ) ;
}
printf ( "Yes\n" ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
printf ( "%I64d%c" , ans[i] , i == n ? '\n' : ' ' ) ;
}
}
int main () {
while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
return 0 ;
}
分析:我们可以选择维护两个线段树。首先将权值离散。一个线段树保存的是以行为下标,副对角线上的对应位置不能到达的最右列的下标。另一个线段树保存的是以列为下标,副对角线上的对应位置不能到达的最下行的下标。然后对于询问是’L’的,我们更新列线段树上对应区间的最大值(最大值恰好对应上面的信息),询问是’U’的同理。
my code:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , cnt
const int MAXN = 400005 ;
const int INF = 0x3f3f3f3f ;
struct Node {
int x , y , f ;
} ;
Node node[MAXN] ;
int a[MAXN] , cnt ;
int lazyL[MAXN << 2] ;
int lazyU[MAXN << 2] ;
int n , m ;
int unique ( int n ) {
sort ( a + 1 , a + n + 1 ) ;
int cnt = 1 ;
for ( int i = 2 ; i <= n ; ++ i ) {
if ( a[i] != a[cnt] ) a[++ cnt] = a[i] ;
}
return cnt ;
}
int search ( int x , int l = 1 , int r = cnt ) {
while ( l < r ) {
int m = ( l + r ) >> 1 ;
if ( a[m] >= x ) r = m ;
else l = m + 1 ;
}
return l ;
}
void build ( int o , int l , int r ) {
lazyL[o] = lazyU[o] = 0 ;
if ( l == r ) return ;
int m = mid ;
build ( lson ) ;
build ( rson ) ;
}
void pushdown ( int o ) {
if ( lazyL[o] ) {
lazyL[ls] = max ( lazyL[ls] , lazyL[o] ) ;
lazyL[rs] = max ( lazyL[rs] , lazyL[o] ) ;
lazyL[o] = 0 ;
}
if ( lazyU[o] ) {
lazyU[ls] = max ( lazyU[ls] , lazyU[o] ) ;
lazyU[rs] = max ( lazyU[rs] , lazyU[o] ) ;
lazyU[o] = 0 ;
}
}
void update ( int L1 , int R1 , int v , int f , int o , int l , int r ) {
if ( L1 <= l && r <= R1 ) {
if ( f == 1 ) lazyU[o] = max ( lazyU[o] , v ) ;
else lazyL[o] = max ( lazyL[o] , v ) ;
return ;
}
int m = mid ;
pushdown ( o ) ;
if ( L1 <= m ) update ( L1 , R1 , v , f , lson ) ;
if ( m < R1 ) update ( L1 , R1 , v , f , rson ) ;
}
int query1 ( int x , int o , int l , int r ) {
if ( l == r ) return lazyU[o] ;
int m = mid ;
pushdown ( o ) ;
if ( x <= m ) return query1 ( x , lson ) ;
else return query1 ( x , rson ) ;
}
int query0 ( int x , int o , int l , int r ) {
if ( l == r ) return lazyL[o] ;
int m = mid ;
pushdown ( o ) ;
if ( x <= m ) return query0 ( x , lson ) ;
else return query0 ( x , rson ) ;
}
void solve () {
char op[5] ;
cnt = 0 ;
a[++ cnt] = 1 ;
for ( int i = 1 ; i <= m ; ++ i ) {
scanf ( "%d%d%s" , &node[i].y , &node[i].x , op ) ;
node[i].f = op[0] == 'U' ;
a[++ cnt] = node[i].x ;
a[++ cnt] = node[i].y ;
}
cnt = unique ( cnt ) ;
build ( root ) ;
for ( int i = 1 ; i <= m ; ++ i ) {
int x = search ( node[i].x ) ;
int y = search ( node[i].y ) ;
if ( node[i].f == 1 ) {
int t = query1 ( y , root ) ;
printf ( "%d\n" , a[x] - a[t] ) ;
update ( y , y , x , 1 , root ) ;
//printf ( "%d %d %d\n" , t + 1 , x , y ) ;
if ( t + 1 <= x ) update ( t + 1 , x , y , 0 , root ) ;
} else {
int t = query0 ( x , root ) ;
printf ( "%d\n" , a[y] - a[t] ) ;
update ( x , x , y , 0 , root ) ;
//printf ( "%d %d %d\n" , t + 1 , y , x ) ;
if ( t + 1 <= y ) update ( t + 1 , y , x , 1 , root ) ;
}
}
}
int main () {
while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
return 0 ;
}
分析:其实就是暴力模拟,先将端点转移到他能遇到的最右端,然后再得到能到达的最左端,然后我们肯定会绕着这两个点转几圈,剩下的长度其实就是mod这两个点的距离。然后根据绕了多少次,确定转移到这两个点中的哪个点。重复上述步骤直到两个端点都是自己为止。由于mod次数最多log(L)(每次长度至少折半),于是算法复杂度为 O(∑ni=1log(li))=O(nlog(MAXL))
my code:
#include <bits/stdc++.h>
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )
const int MAXN = 200005 ;
pair < int , int > p[MAXN] ;
int idx[MAXN] ;
int n , m ;
int Lsearch ( int x , int l = 1 , int r = n ) {
while ( l < r ) {
int m = ( l + r ) >> 1 ;
if ( p[m].first >= x ) r = m ;
else l = m + 1 ;
}
return l ;
}
int Rsearch ( int x , int l = 1 , int r = n ) {
while ( l < r ) {
int m = ( l + r + 1 ) >> 1 ;
if ( p[m].first <= x ) l = m ;
else r = m - 1 ;
}
return l ;
}
void deal () {
int x , l ;
scanf ( "%d%d" , &x , &l ) ;
x = idx[x] ;
while ( 1 ) {
int t = Rsearch ( p[x].first + l ) ;
if ( p[t].first - p[x].first <= l ) {
l -= p[t].first - p[x].first ;
x = t ;
}
int A = Lsearch ( p[x].first - l ) ;
if ( A == x ) {
printf ( "%d\n" , p[x].second ) ;
return ;
}
int cnt = l / ( p[x].first - p[A].first ) ;
l %= ( p[x].first - p[A].first ) ;
if ( cnt & 1 ) x = A ;
}
}
void solve () {
for ( int i = 1 ; i <= n ; ++ i ) {
scanf ( "%d" , &p[i].first ) ;
p[i].second = i ;
}
sort ( p + 1 , p + n + 1 ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
idx[p[i].second] = i ;
}
for ( int i = 1 ; i <= m ; ++ i ) {
deal () ;
}
}
int main () {
while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
return 0 ;
}
分析:首先,如果是在一个边双连通分量的,我们可以将其构造成强连通分量,于是问题只在桥了。对于桥,简单的做法,就是对于操作a->b,我们可以对up[lca(a,b)]–,up[a]++,down[lca(a,b)]–,down[b]++,然后最后按照树的bfs序倒着还原树上的前缀和即可。如果最后一个点(表示一条边),既有被标记为上升,又有被标记为下降,那么就是不合法的,反之则是合法的。
然后这题我的做法比较傻逼……我用了一个树链剖分来实现了这一个过程……
my code:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , n
const int MAXN = 200005 ;
const int MAXE = 400005 ;
struct Edge {
int v , n ;
Edge () {}
Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;
struct Node {
int u , v ;
} ;
struct BCC {
Edge E[MAXE] ;
int H[MAXN] , cntE ;
int bcc[MAXN] , bcc_cnt ;
int low[MAXN] , dfn[MAXN] , dfs_clock ;
int S[MAXN] , top ;
void init () {
cntE = top = bcc_cnt = dfs_clock = 0 ;
clr ( bcc , 0 ) ;
clr ( dfn , 0 ) ;
clr ( H , -1 ) ;
}
void addedge ( int u , int v ) {
E[cntE] = Edge ( v , H[u] ) ;
H[u] = cntE ++ ;
}
void tarjan ( int u , int fa ) {
low[u] = dfn[u] = ++ dfs_clock ;
S[top ++] = u ;
int flag = 1 ;
for ( int i = H[u] ; ~i ; i = E[i].n ) {
int v = E[i].v ;
if ( v == fa && flag ) {
flag = 0 ;
continue ;
}
if ( !dfn[v] ) {
tarjan ( v , u ) ;
low[u] = min ( low[u] , low[v] ) ;
}
else low[u] = min ( low[u] , dfn[v] ) ;
}
if ( low[u] == dfn[u] ) {
++ bcc_cnt ;
while ( 1 ) {
int v = S[-- top] ;
bcc[v] = bcc_cnt ;
if ( v == u ) break ;
}
}
}
void solve ( int n ) {
for ( int i = 1 ; i <= n ; ++ i ) {
if ( !dfn[i] ) tarjan ( i , -1 ) ;
}
}
} ;
Edge E[MAXE] ;
int H[MAXN] , cntE ;
Node e[MAXN] ;
BCC c ;
int top[MAXN] ;
int siz[MAXN] ;
int pre[MAXN] ;
int dep[MAXN] ;
int pos[MAXN] ;
int son[MAXN] ;
int tree_idx ;
int n , m , q ;
int p[MAXN] ;
int val[2][MAXN] ;
map < pair < int , int > , int > mp ;
void init () {
cntE = 0 ;
tree_idx = 0 ;
clr ( pre , 0 ) ;
clr ( dep , 0 ) ;
clr ( H , -1 ) ;
}
void addedge ( int u , int v ) {
E[cntE] = Edge ( v , H[u] ) ;
H[u] = cntE ++ ;
}
void dfs ( int u ) {
siz[u] = 1 ;
son[u] = 0 ;
for ( int i = H[u] ; ~i ; i = E[i].n ) {
int v = E[i].v ;
if ( v == pre[u] ) continue ;
pre[v] = u ;
dep[v] = dep[u] + 1 ;
dfs ( v ) ;
siz[u] += siz[v] ;
if ( siz[son[u]] < siz[v] ) son[u] = v ;
}
}
void rebuild ( int u , int top_element ) {
top[u] = top_element ;
pos[u] = ++ tree_idx ;
if ( son[u] ) rebuild ( son[u] , top_element ) ;
for ( int i = H[u] ; ~i ; i = E[i].n ) {
int v = E[i].v ;
if ( v != pre[u] && v != son[u] ) rebuild ( v , v ) ;
}
}
void Update ( int x , int y ) {
while ( top[x] != top[y] ) {
if ( dep[top[x]] > dep[top[y]] ) {
val[0][pos[top[x]]] ++ ;
val[0][pos[x] + 1] -- ;
x = pre[top[x]] ;
} else {
val[1][pos[top[y]]] ++ ;
val[1][pos[y] + 1] -- ;
y = pre[top[y]] ;
}
}
if ( x == y ) return ;
if ( dep[x] < dep[y] ) {
val[1][pos[x] + 1] ++ ;
val[1][pos[y] + 1] -- ;
} else {
val[0][pos[y] + 1] ++ ;
val[0][pos[x] + 1] -- ;
}
}
int find ( int x ) {
return p[x] == x ? x : ( p[x] = find ( p[x] ) ) ;
}
void solve () {
init () ;
c.init () ;
mp.clear () ;
clr ( val , 0 ) ;
for ( int i = 1 ; i <= m ; ++ i ) {
scanf ( "%d%d" , &e[i].u , &e[i].v ) ;
c.addedge ( e[i].u , e[i].v ) ;
c.addedge ( e[i].v , e[i].u ) ;
}
c.solve ( n ) ;
n = c.bcc_cnt ;
for ( int i = 1 ; i <= n ; ++ i ) {
p[i] = i ;
}
for ( int i = 1 ; i <= m ; ++ i ) {
int u = c.bcc[e[i].u] ;
int v = c.bcc[e[i].v] ;
if ( u == v ) continue ;
if ( u > v ) swap ( u , v ) ;
if ( mp.count ( make_pair ( u , v ) ) ) continue ;
mp[make_pair ( u , v )] = 1 ;
addedge ( u , v ) ;
addedge ( v , u ) ;
p[find ( u )] = find ( v ) ;
}
for ( int i = 1 ; i <= n ; ++ i ) if ( !pre[i] ) {
dfs ( i ) ;
rebuild ( i , i ) ;
}
int ok = 1 ;
for ( int i = 1 ; i <= q ; ++ i ) {
int u , v ;
scanf ( "%d%d" , &u , &v ) ;
u = c.bcc[u] ;
v = c.bcc[v] ;
if ( u == v || !ok ) continue ;
if ( find ( u ) != find ( v ) ) ok = 0 ;
else Update ( u , v ) ;
}
for ( int i = 2 ; i <= n ; ++ i ) {
val[0][i] += val[0][i - 1] ;
val[1][i] += val[1][i - 1] ;
if ( val[0][i] && val[1][i] ) ok = 0 ;
}
printf ( "%s\n" , ok ? "Yes" : "No" ) ;
}
int main () {
while ( ~scanf ( "%d%d%d" , &n , &m , &q ) ) solve () ;
return 0 ;
}