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 $

#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,所以这种区间一定不优。
#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模拟一下就好了

#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显然无解。

#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("");
    }
}

原文地址:https://www.cnblogs.com/yijan/p/cf1239.html

时间: 2024-08-26 04:57:17

CF Round 594的相关文章

Codeforces Round #594 (Div. 2)

Codeforces Round #594 (Div. 2) A. Integer Points 题意:给出若干个\(y = x + p\) 和 \(y = -x + q\) 求它们交点坐标为整数的个数 思路:\(y = x + p\) 与 \(y = -x + q\) 的交点为 \(\left(\frac{q - p}{2}, \frac{p + q}{2}\right)\) 即只用统计p和q数组中的奇偶数即可 AC代码 #include <algorithm> #include <i

CF Round #629

CF Round #629 A.数学 给定a,b,现在问你最小让a加多少使得a能被b整除,可以为0 即算(b-(a%b))%b B.数学 给定n和k 问以n-2个a和2个b组成的串中,以字典序升序排列,问第k个是几 这个有点类似康托展开,这个简化了很多 首先考虑第一个b,它处在从左往右数第p位,那么无论第二个b怎么放,它最大是(p-1)*p/2 所以只要找到第一个b为u,第二个b从u-1开始,每往后移一位就小一,找k即可 C.数学 给定n和x x是一串开头必为2,由0,1,2组成的字符串,一共有

cf Round#273 Div.2

题目链接,点击一下 Round#273 Div.2 ================== problem A Initial Bet ================== 很简单,打了两三场的cf第一次在10分钟内过题 判平均数,且注意b为正 1 #include<iostream> 2 using namespace std; 3 int main() 4 { 5 int res = 0,n; 6 for(int i = 0; i < 5; i++) 7 { 8 cin>>

【codeforces】【比赛题解】#915 Educational CF Round 36

虽然最近打了很多场CF,也涨了很多分,但是好久没写CF的题解了. 前几次刚刚紫名的CF,太伤感情了,一下子就掉下来了,不懂你们Div.1. 珂学的那场我只做了第一题--悲伤. 这次的Educational Round打的还可以,虽然吧没有涨分(因为我是紫色的啊). 做了前4题,后面3题也比较简单,陆续也做完了. 所以心情好,来写一篇题解! [A]花园 题意: 长度为\(k\)的线段,用若干个长度为\(a_i\)的线段,正好覆盖.(\(a_i|k\)) 给定\(n\)个\(a_i\),求出最小的\

【codeforces】【比赛题解】#854 CF Round #433 (Div.2)

cf一如既往挺丧 看丧题点我! [A]分数 Petya是数学迷,特别是有关于分数的数学.最近他学了所谓一个分数被叫做"真分数"当且仅当其分子小于分母,而一个分数被叫做"最简分数"当且仅当其分子分母互质.在闲暇时间,Petya在用计算器研究:如何把最简真分数转换为小数等问题.有一天他不小心把除号(÷)按成了加号(+),导致他得到了分子与分母的和.Petya想要得到他原来的分数,但他很快发现这不是唯一的.所以现在他想要知道最大的最简真分数使得其分子与分母的和为n. 输入

cf Round 613

A.Peter and Snow Blower(计算几何) 给定一个点和一个多边形,求出这个多边形绕这个点旋转一圈后形成的面积.保证这个点不在多边形内. 画个图能明白 这个图形是一个圆环,那么就是这个点距离多边形边缘最远的距离形成的圆面积减去这个点距离多边形边缘最近的距离形成的圆面积.我们可以得出距离最远的点一定是多边形的顶点.而距离最近的点不一定是多边形的顶点,但是在多边形的边上.我们用勾股定理判断点与每条边形成的三角形的两边角.如果有一个边角是钝角,则表示距离最近的点是顶点.如果都是锐角,则

【codeforces】【比赛题解】#849 CF Round #431 (Div.2)

cf的比赛越来越有难度了--至少我做起来是这样. 先看看题目吧:点我. 这次比赛是北京时间21:35开始的,算是比较良心. [A]奇数与结束 "奇数从哪里开始,又在哪里结束?梦想从何处起航,它们又是否会破灭呢?" 给定一个长度为n的序列.确定能不能将序列分成奇数个长度为奇数的非空字串,而且这其中每个子串以奇数开头,以奇数结尾.可以只分成一个(1也是奇数). 输入 第一行一个正整数n,表示序列长度. 第二行n个整数,表示序列中的元素. 输出 输出"Yes"或"

CF Round #631 题解

\(Codeforces\) \(Round\) \(631\) A.Dreamoon and Ranking Collection 题目大意: \(n\)轮比赛,每轮比赛排名已经给出,还可以进行额外的\(m\)场比赛 问:所有比赛进行完后,最多可以收集到从\(1\)开始的多少个连续名次 题解: 用一个数组统计一下排名的出现情况,然后扫一遍添加\(m\)个缺失名次即可 代码: #include<iostream> #include<cstdio> #include<cstdl

【解题报告】CF Round #320 (Div. 2)

Raising Bacteria 题意:盒子里面的细菌每天会数量翻倍,你可以在任意一天放任意多的细菌,最后要使得某天盒子里面的细菌数量等于x,求至少要放多少个细菌 思路:显然,翻倍即为二进制左移一位,那么放入一个细菌,到第二天就变成2个二进制下即为1->10对于任意二进制数 如:1001110,只需要在第一天放1个,第4.5.6天各放一个,再等一天,就可以了.所以答案显然是x在二进制下1的个数. Finding Team Member 题意:2n个人,每两个人组合的队伍有aij的力量.每个人都希