Codeforces Round #244 (Div. 2)

这场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 200005

struct 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 600005
struct 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值