这场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 ;
}