CF Round 594

CF Round 594(Div1) (A~D)简要题解

开学基本打不了cf了啊。。

A Ivan the Fool and the Probability Theory

对于$1 \times n$的情况,稍微推一推式子发现是斐波那契数列的两倍(因为第一个位置可以是0可以是1,就是两倍了,否则是一倍)。

考虑第一行,第一行有两种情况:

  • 如果第一行是 01010… 交错的,那么 0 开头可以看成一种颜色,1 开头可以看成一种颜色。然后就成了一个竖着的$1 \times n$的情况。然后考虑第 1x1 的位置可以是0可以是1,所以这样的情况数量是$2 f(n)$
  • 其他情况,第一行有$2f(m) - 2$种情况,这些时候由于第一行有一对相邻的相同,从相同的入手,画一下会发现可以唯一确定下一行,于是唯一确定了这个矩阵。

所以总情况数量是$2f(n) + 2f(m) - 2$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
using namespace std;
#define int long long
typedef long long ll;
#define MAXN 200006
#define MAXM 450
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define inf 0x3f3f3f3f
#define cmx( a , b ) a = max( a , b )
#define cmn( a , b ) a = min( a , b )
#define upd( a , b ) ( a = ( a + b ) % P )
#define P 1000000007
void read( signed& x ) {
scanf("%d",&x);
}
void read( ll& x ) {
scanf("%lld",&x);
}
int n , m , x0;
#define swap( a , b ) a=a^b,b=a^b,a=a^b
int f[MAXN];
signed main() {
f[0] = f[1] = 1;
cin >> n >> m;
for( int i = 2 ; i <= max( n , m ) ; ++ i ) f[i] = ( f[i - 1] + f[i - 2] ) % P;
cout << ( ( f[n] * 2 ) % P + ( f[m] * 2 ) % P - 2 + P ) % P << endl;
}

B. The World Is Just a Programming Task (Hard Version)

首先,如果左括号和右括号的数量都不一样了,显然 puts("0")

否则,我们一定可以得到一种循环移位方法使得当前是一个合法的括号序列。具体而言,可以把括号加入栈,然后一定是 )))((( 的形式。就可以根据这个循环移位了。

现在我们得到了一个合法的括号序列。如果把 ( 看成+1 , ) 看成-1,那么前缀和一定不会小于0,且前缀和一定是从0到0的。(图可以看cf官方题解)

然后有一个在 easy ver 的结论,最小值出现的次数就是答案。这个结论显然成立其实就是考虑移动一定是把一个合法的括号子串从最前面移动到最后面。当括号序列不合法先移成合法然后就一样了。

所以题目就简化成了,交换两个括号使得前缀和最小值个数尽量多。

而交换两个括号,如果交换一个前面的 ) 和一个后面的 ( 显然没用啊!

所以肯定是交换前面的一个 ( 和后面的一个 ) , 这个在前缀和序列上就体现为区间-2

选择一个区间-2后,最小值变成了 -2 / -1 / 0

  • 最小值变成了 - 2 。 如果出现了-2一定是把0减少成-2,由于原来的答案是0的个数,现在的答案变成了-2的个数。即使最优,也只是把所有0变成了-2,没有价值。
  • 最小值变成了 - 1 。 由于最小值变成-1而不是-2,区间内肯定没有0。我们按照0把整个序列分段,那么一定不会选择区间横跨在两段之间。然而一个显然的贪心是如果合法,减少的数字尽量多肯定尽量优秀。
  • 最小值变成了 0 。同第二种情况,只能在两个1之间。同样方法做就好。这里注意其实不用判断这一段之间是否有0,因为差分肯定是1或者-1,所以这种区间一定不优。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
using namespace std;
//#define int long long
typedef long long ll;
#define MAXN 600006
#define MAXM 450
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define inf 0x3f3f3f3f
#define cmx( a , b ) a = max( a , b )
#define cmn( a , b ) a = min( a , b )
#define upd( a , b ) ( a = ( a + b ) % P )
#define swap( a , b ) a = a ^ b , b = a ^ b , a = a ^ b
#define P 1000000007
void read( signed& x ) {
scanf("%d",&x);
}
void read( ll& x ) {
scanf("%lld",&x);
}
int n , p , x0;
char s[MAXN];
int S[MAXN];
stack<pair<int,char>> stk;
vector<int> x , y;
signed main() {
read( n );
scanf("%s",s + 1);
int cc = 0;
for( int i = 1 ; i <= n ; ++ i ) cc += ( s[i] == '(' ? 1 : -1 );
if( cc ) return puts("0\n1 1");
for( int i = 1 ; i <= n ; ++ i ) {
if( !stk.empty() && s[i] == ')' && stk.top().second == '(' ) stk.pop();
else stk.push( mp( i , s[i] ) );
}
int las = 1;
while( !stk.empty() && stk.top().se == '(' ) las = stk.top().fi , stk.pop();
-- las;
for( int i = 1 ; i <= n ; ++ i ) S[i] = S[i - 1] + (s[(i + las - 1 + n) % n + 1] == '(' ? 1 : -1);
int res0 = 0;
for( int i = 0 ; i <= n ; ++ i ) if( S[i] == 0 ) x.pb( i ) , ++ res0; else if( S[i] == 1 ) y.pb( i );
-- res0;
int ans = res0 , cur = 0;
pii ANS = mp(1 , 1);
for( int i = 0 ; i < x.size() - 1 ; ++ i ) {
cur = 0;
for( int j = x[i] + 1 ; j < x[i + 1] ; ++ j ) if( S[j] == 1 ) ++ cur;
if( cur > ans ) ans = cur , ANS = mp( x[i] + 1 , x[i + 1] );
ans = max( ans , cur );
}
for( int i = 0 ; i < y.size() - 1 ; ++ i ) {
cur = 0;
for( int j = y[i] + 1 ; j < y[i + 1] ; ++ j ) if( S[j] == 2 ) ++ cur;
if( cur + res0 > ans ) ans = cur + res0 , ANS = mp( y[i] + 1 , y[i + 1] );
}
cout << ans << endl;
ANS.fi += las - 1 , ANS.fi %= n , ANS.fi ++;
ANS.se += las - 1 , ANS.se %= n , ANS.se ++;
cout << ANS.fi << ' ' << ANS.se;
}

C Queue in the Train

题解的那种模拟就跑过去了。

这个就看题解吧。。就是用三个set模拟一下就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define MAXN 100006
#define tup tuple<int,int,int>
int n , p;
int t[MAXN];
set<tup> evt;
set<int> want , que;
int qt = 0 , ct = 0 , ans[MAXN];
signed main() {
cin >> n >> p;
for( int i = 1 ; i <= n ; ++ i ) scanf("%lld",&t[i]) , evt.insert( make_tuple( t[i] , 0 , i ) );
while( !evt.empty() ) {
tup th = *( evt.begin() ); evt.erase( evt.begin() );
ct = get<0>(th);
if( !get<1>(th) )
want.insert( get<2>(th) );
else
que.erase( get<2>(th) ) , ans[get<2>(th)] = ct;
if( want.empty() ) continue;
if( que.empty() || *( want.begin() ) < *( que.begin() ) )
evt.insert( make_tuple( max( qt , ct ) + p , 1 , *(want.begin()) ) ) , qt = max( qt , ct ) + p , que.insert( *(want.begin()) ) , want.erase( want.begin() );
}
for( int i = 1 ; i <= n ; ++ i ) printf("%lld ",ans[i]);
}

D Catowice City

由于左边的 i 向右边的 i 有边,相当于告诉你对于$i$它要么选择左边的要么选择右边的。

一条从左边的 u 连向右边 v 的边相当于限定左边选择了 uv 只能选择左边,这种情况可以给 uv 连一条有向边表示如果 u 是一个左边, v 也必须是左边。

然后可以缩点,找一个入度为0的scc设置为右边,其他都放在右边就好了。

当然,如果只有一个scc显然无解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
using namespace std;
//#define int long long
typedef long long ll;
#define MAXN 1000006
#define MAXM 450
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define inf 0x3f3f3f3f
#define cmx( a , b ) a = max( a , b )
#define cmn( a , b ) a = min( a , b )
#define upd( a , b ) ( a = ( a + b ) % P )
#define swap( a , b ) a = a ^ b , b = a ^ b , a = a ^ b
#define P 1000000007
void read( signed& x ) {
scanf("%d",&x);
}
void read( ll& x ) {
scanf("%lld",&x);
}
int n , m;
vector<int> G[MAXN]; int ind[MAXN];
int dfn[MAXN] , low[MAXN] , clo , ins[MAXN] , stk[MAXN] , tp = 0;
int indg[MAXN] , bl[MAXN] , scc; vector<int> sc[MAXN];
void tarjan( int u ) {
dfn[u] = low[u] = ++ clo , ins[u] = 1 , stk[++ tp] = u;
for( int v : G[u] ) {
if( !dfn[v] ) tarjan( v ) , low[u] = min( low[u] , low[v] );
else if( ins[v] ) low[u] = min( low[u] , dfn[v] );
}
if( dfn[u] == low[u] ) {
int x; ++ scc;
do {
x = stk[tp--] , bl[x] = scc , ins[x] = 0 , sc[scc].pb( x );
} while( x != u );
}
}
vector<int> ans1 , ans2;
int main() {
int t; cin >> t;
while( t-- ) {
read( n ) , read( m );
for( int i = 1 ; i <= n ; ++ i ) G[i].clear() , low[i] = dfn[i] = ins[i] = stk[i] = bl[i] = indg[i] = 0;
for( int i = 1 , u , v ; i <= m ; ++ i ) {
read( u ) , read( v );
G[u].pb( v );
}
for( int i = 1 ; i <= scc ; ++ i ) sc[i].clear();
scc = 0;
for( int i = 1 ; i <= n ; ++ i ) if( !dfn[i] ) tarjan( i );
for( int i = 1 ; i <= n ; ++ i )
for( int v : G[i] ) if( bl[v] != bl[i] )
++ indg[bl[v]];
if( scc == 1 ) { puts("NO"); continue; }
puts("YES");
int flg = 0;
ans1.clear() , ans2.clear();
for( int i = 1 ; i <= scc ; ++ i )
if( flg || indg[i] )
for( int v : sc[i] ) ans1.pb( v );
else { for( int v : sc[i] ) ans2.pb( v ); flg = 1; }
printf("%d %d\n",ans1.size() , ans2.size( ));
for( int v : ans1 ) printf("%d ",v); puts("");
for( int v : ans2 ) printf("%d ",v); puts("");
}
}
文章作者: yijan
文章链接: https://yijan.co/old52/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog