Codeforces Round #244 (Div. 2)

来源:互联网 发布:邮箱大师 mac 编辑:程序博客网 时间:2024/06/01 08:13

这场CF题目的难度不正常啊,很不正常啊!!E题这么简单!!出题人怎么想的 ...


A. Police Recruits

http://codeforces.com/problemset/problem/427/A

题意 : 给你n个数,大于0的数表示有a[i]个police可以派遣, -1 表示这里发生了犯罪需要前面的一个police来平息犯罪 。求有多少犯罪平息不了


思路: 用一个变量sum表示当前可以有多少police可以用,遇到整数则sum+=a[i] , 遇到-1,若sum为0,那么这个犯罪则平息不了,不为0,则sum--。


#include <stdio.h>#include <string.h>int a[100005] ;int main(){int n ;while( scanf( "%d" , &n ) != EOF ) {int ans = 0 ;int sum = 0 ;for( int i = 1 ; i <= n ; i ++ ) {int a ;scanf( "%d" , &a ) ;if( a == -1 ) {if( sum == 0 ) ans ++ ;else sum -- ;}else{sum += a ;}}printf( "%d\n" , ans ) ;}return 0 ;}

B. Prison Transfer

http://codeforces.com/problemset/problem/427/B

题意: 给你n个数,求出有多少长度为c的区间,他的最大值是小于等于t的


思路: 用线段树,树状数组,RMQ什么的都可以搞,我是用单调队列搞的

#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define MAXN 200005struct Queue {    int top , rear ;    struct node {        int time , val ;    } Q[MAXN];    void clear() {        rear = 0;        top = 1 ;    }    void push_back ( int time , int e ) {        while ( top <= rear && Q[rear].val <= e ) rear -- ;        Q[++rear].time = time;        Q[rear].val = e;    }    void pop_top ( int time ) {        while ( Q[top].time <= time ) top ++ ;    }    int top_val() {        return Q[top].val;    }}q;int main(){int n , t , c ;while( scanf( "%d%d%d" , &n , &t , &c ) != EOF ) {q.clear() ;for( int i = 1 ; i <= c ; i ++ ) {int a ;scanf( "%d" , &a ) ;q.push_back( i , a ) ;}int cnt = 0 ;if( q.top_val() <= t ) cnt ++ ;int j = 1 ;for( int i = c + 1 ; i <= n ;i  ++ ) {int a ;scanf( "%d" , &a ) ; q.push_back( i , a ) ;q.pop_top( j ++ ) ;if( q.top_val() <= t ) cnt ++ ;}printf( "%d\n" , cnt ) ;}return 0 ;}

C. Checkposts

http://codeforces.com/problemset/problem/427/C

题意 : 给你n个点,m条有向边,每个点都有权值,你需要挑选若干个点建立post,要求使每个点都可以到达至少一个post,也至少可以由一个post到达。求挑选的点的最小权值和以及方案数。


思路: 求强连通分量,对于每个连通分量,我们去其中的点的最小值,这样就能达到权值和最小。方案数就每个连通分量中权值为最小权值的点的个数的乘积。注意下不要溢出,不要RE就可以了。


#include<cstdio>#include <cstring>#include <iostream>using namespace std;#define maxn 200005#define maxm 600005struct Edge{    int v;    int next;};Edge edge[maxm];//边的集合int node[maxn];//顶点集合int now[maxn]; //新图的顶点集合int instack[maxn];//标记是否在stack中int stack[maxn];int Belong[maxn];//各顶点属于哪个强连通分量int DFN[maxn];//节点u搜索的序号(时间戳)int LOW[maxn];//u或u的子树能够追溯到的最早的栈中节点的序号(时间戳)int n, m;//n:点的个数;m:边的条数int cnt_edge;//边的计数器int Index;//序号(时间戳)int top;int Bcnt;//有多少个强连通分量int Min[maxn] ; // 每个强连通分量的最小权值点int Cnt[maxn] ; // 每个强连通分量的最小权值点的个数void init(){cnt_edge=0;memset(node,-1,sizeof(node));}void add_edge(int u, int v)//邻接表存储{    edge[cnt_edge].next = node[u];    edge[cnt_edge].v = v;    node[u] = cnt_edge++;}void tarjan(int u){    int i,j;    int v;    DFN[u]=LOW[u]=++Index;    instack[u]=true;    stack[++top]=u;    for (i = node[u]; i != -1; i = edge[i].next)    {        v=edge[i].v;        if (!DFN[v])//如果点v没被访问        {            tarjan(v);            if (LOW[v]<LOW[u])                LOW[u]=LOW[v];        }        else//如果点v已经被访问过            if (instack[v] && DFN[v]<LOW[u])                LOW[u]=DFN[v];    }    if (DFN[u]==LOW[u])    {        Bcnt++;        do        {            j=stack[top--];            instack[j]=false;            Belong[j]=Bcnt;        }        while (j!=u);    }}void solve(){    int i;    top=Bcnt=Index=0;    memset(DFN,0,sizeof(DFN));    memset(LOW,0,sizeof(LOW));    for (i=1;i<=n;i++)        if (!DFN[i])            tarjan(i);}int out[maxn],flag[maxn],in[maxn];void suodian()//邻接表形式的新图{int v;memset(now,-1,sizeof(now));memset(out,0,sizeof(out));memset(in,0,sizeof(in));memset(flag,-1,sizeof(flag));//用于判断重边,有些图需要排除重边,而有些则不用,视情况而定for(int u=1;u<=n;u++){for(int i=node[u];i!=-1;i=edge[i].next){v=edge[i].v;if(Belong[u]!=Belong[v]&&flag[Belong[u]]!=Belong[v])//缩点建新图,并统计缩点后各点的出度和入度{flag[Belong[u]]=Belong[v];edge[cnt_edge].v=Belong[v];edge[cnt_edge].next=now[Belong[u]];now[Belong[u]]=cnt_edge++;out[Belong[u]]++;in[Belong[v]]++;}}}}__int64 val[100005] ;int main(){while( scanf( "%d" , &n ) != EOF ) {for( int i =1 ; i <= n; i ++ ) scanf( "%I64d" , &val[i] ) ;scanf( "%d" , &m ) ;init() ;while( m -- ) {int u , v ;scanf( "%d%d" , &u , &v ) ;add_edge( u , v ) ;}solve() ;suodian() ;memset( Min , 0x3f , sizeof(Min) ) ;for( int i = 1 ; i <= n ; i ++ ) {int be = Belong[i] ;if( val[i] < Min[be] ) {Min[be] = val[i] ;Cnt[be] = 1 ;}else if( val[i] == Min[be] ) {Cnt[be] ++ ;}}__int64 ans1 = 0 , ans2 = 1 ;for( int i = 1 ; i <= Bcnt ; i ++ ) {ans1 += Min[i] ;ans2 *= Cnt[i] ;ans2 %= 1000000007 ;}printf( "%I64d %I64d\n" , ans1 , ans2 ) ;}return 0 ;}

D. Match & Catch

http://codeforces.com/problemset/problem/427/D

题意: 给你两个字符串,你需要找到长度最短的,公共字串,并且这个字串在串1中只出现一次,在串2中只出现一次。


思路:委屈不会后缀数组哭了... 不会做

E. Police Patrol

http://codeforces.com/problemset/problem/427/E

题意: 有n个犯罪地点,我们需要选择一个地方作为警察局,从这里出发抓住所有的罪犯,警车做多可以装m个罪犯。求要抓住所有的罪犯的路程最小值是多少。

思路: 首先选择的地点肯定是这n个犯罪地点中的其中一个。我们假设选择的地点在两个犯罪地点x,y的中间,那么假设x左边的点要多余y右边的点,那么我们将建造警察局的点移到x至少不会差于原来的情况。其他情况自己考虑。

那么我们就集中考虑把警察局建在犯罪地点的情况。

我们将整个坐标轴分成n-1份,假设我们选择x作为警察局,那么我们可以知道,前m段肯定只会走一个来回,再接下来的m段肯定会走2个来回 ... 以此类推

那么我们就可以预处理出一个数组L[i] 为以i作为警察局,抓掉i左边的所有罪犯的路径是多少

同样预处理出R[i] , 最后只要枚举这n个点就可以了

#include <stdio.h>#include <string.h>#include <algorithm>#include <math.h>using namespace std;__int64 L[1000005] , R[1000005];__int64 a[1000005] ;int main(){    int n , m ;    while( scanf( "%d%d" , &n , &m ) != EOF ) {        for( int i = 1 ; i <= n; i ++ )            scanf( "%I64d" , &a[i] ) ;        if( m >= n ) {            printf( "%I64d\n" , ( a[n] - a[1] ) * 2 ) ;            continue ;        }        L[1] = 0 ;        for( int i = 2 ; i <= n ; i ++ )            L[i] = L[i-1] + (__int64)ceil( 1.0 * ( i - 1 ) / m ) * 2 * ( a[i] - a[i-1] ) ;         R[n] = 0 ;        for( int i = n - 1 ; i >= 1 ; i -- )            R[i] = R[i+1] + (__int64)ceil( 1.0 * ( n - i ) / m ) * 2 * ( a[i+1] - a[i] ) ;        __int64 Min = 99999999999999999LL ;        for( int i = 1 ; i <= n ; i ++ ) {            Min = min( L[i] + R[i] , Min ) ;        }        printf( "%I64d\n" , Min ) ;    }     return 0 ;}


0 0
原创粉丝点击