Codeforces Beta Round #6 (Div. 2 Only)

A.Triangle

一到四题都比较简单,就直接贴代码了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 4;
int length[maxn];

bool triangle(int no){
    int i, j = 0, a[3];
    for(i = 0; i < 4; i++) 
	if(i != no) a[j++] = length[i];
    for(i = 0; i < 3; i++)
	if(a[i] == 0) return false;
    sort(a,a+3);
    if(a[0] + a[1] > a[2]) return true;
    else return false;
}

bool segment(int no){
    int i, j = 0, a[3];
    for(i = 0; i < 4; i++) 
	if(i != no) a[j++] = length[i];
    for(i = 0; i < 3; i++)
	if(a[i] == 0) return true;
    sort(a,a+3);
    if(a[0] + a[1] == a[2]) return true;
    else return false;
}

int main(){
    int i, j, k, n = 4;
    for(i = 0; i < n; i++)
	scanf("%d",length+i);
    
    int ok = 0;
    for(i = 0; i < n; i++)
	if(triangle(i)){
	    ok = 1;
	    break;
	}

    if(ok){
	printf("TRIANGLE\n");
	return 0;
    }

    ok = 0;
    for(i = 0; i < n; i++) 
	if(segment(i)){
	    ok = 1;
	    break;
	}

    if(ok){
	printf("SEGMENT\n");
	return 0;
    }
    
    printf("IMPOSSIBLE\n");
    return 0;
}

B. President's Office

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const int maxn = 120;
const int dx[] = {0,0,-1,+1};
const int dy[] = {-1,+1,0,0};

char r;
int n, m;
int color[1000];
char matrix[maxn][maxn];

char get(){
    char c = getchar();
    while((c < 'A' || c > 'Z') && c != '.')
	c = getchar();
    return c;
}

int main(){
    int i, j, k;
    scanf("%d%d",&n,&m);
    r = get();
    memset(color,0,sizeof(color));
    for(i = 0; i < n; i++)
	for(j = 0; j < m; j++)
	    matrix[i][j] = get();

    color[r] = 1;
    color['.'] = 1;

    for(i = 0; i < n; i++)
	for(j = 0; j < m; j++)
	    if(matrix[i][j] == r){
		for(k = 0; k < 4; k++){
		    int u = i + dx[k];
		    int v = j + dy[k];
		    if(u >= 0 && u < n)
		    if(v >= 0 && v < m){
			color[matrix[u][v]]++;
		/*	while(matrix[u][v] == '.'){
			    u = u + dx[k];
			    v = v + dy[k];
			}
			*/
		    }
		}
	    }

    int ans  = 0;
    for(i = 0; i < 1000; i++)
	if(color[i])
	    ans++;
    printf("%d\n",ans-2);
    return 0;
}

C. Alice, Bob and Chocolate

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const int maxn = 120000;
int t[maxn], sum[maxn];

int main(){
    int i, j, n, a, b;
    scanf("%d",&n);
    for(i = 0; i < n; i++)
	scanf("%d",t+i);

    int left, right;
    left = right = 0;
    a = -1, b = n;

    while(a+1 != b){
	if(left <= right){
	    left += t[++a];
	}else{
	    right += t[--b];
	}
	//printf("%d %d\n",left,right);
    }

    printf("%d %d\n",a+1, n-b);
    return 0;
}

D. Lizards and Basements 2

这题可以用dp解决的,不过当初图方便写了搜索,其实写得比dp长好多

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

const int maxn = 1000;
int  n, a, b;
int h[maxn];
int cur[maxn];
int now[maxn];
int ans = maxn;

bool dfs(int who, int num){
    int i, j;
    if(num >= ans) return false;
    if(who == n){
	if(h[n] >= 0) return false;
	ans = num;
	for(i = 2; i < n; i++)
	    now[i] = cur[i];
	return true;
    }else{
	int begin = ((h[who-1]+b-1)/b);
	if(begin < 0) begin = 0;
	for(j = begin; j <= 16; j++)
	    if(h[who -1] - b * j >= 0) continue;
	    else {
		if(j + num >= ans) break;
		if(who == n - 1 && h[who+1] - b*j >= 0) continue;
		cur[who] = j;
		h[who-1] -= b * j;
		h[who] -= a * j;
		h[who+1] -= b * j;

		bool ok = dfs(who+1,num+j);
		h[who-1] += b * j;
		h[who] += a * j;
		h[who+1] += b * j;
                if(h[who-1] - b*j < 0 && h[who+1] - b*j < 0 && h[who] - a*j < 0) break;
	    }
    }
    return false;
}

int main(){
    int i, j, sum = 0;
    scanf("%d%d%d",&n,&a,&b);
    for(i = 1; i <= n; i++)
	scanf("%d",h+i);
    dfs(2,0);
    printf("%d\n",ans);
    for(i = 1; i <= n; i++)
	for(j = 1; j <= now[i]; j++){
	    sum++;
	    if(sum  == ans) printf("%d\n",i);
	    else printf("%d ",i);
	}
    return 0;
}


E. Exposition

第五题 我要是再直接贴代码,这遍文章就没法看了。所以,第五题会讲得很长(如果有时间 ,我会把前面的详细题解也补一下的)

题意:给定一个序列,求序列中最长的连续的数,并且其中,最大和最小的差不超过给定的k

输入:第一行:n,k(1<=n<=10^5,0<=k<=10^6)分别表示序列的长度,和最在的差值。

          第二行:给定的序列

输出:第一行:两个数分别表示最大的长度,和有几个这样的区间

           以下每行两个数,分别代表所求的区间

题解:首先,我们可以知道如果区间 [ l, r+1 ] 满足条件,那么区间 [l,r] 肯定满足条件。同样的如果区间[ l-1, r]满足条件,那么 [l,r]也满足条件。所以,如果固定起点,或者固定终点,则最大的长度满足单调性。所以我们有以下的方法。

            方法一:

        枚举起点,二分终点,或者说是二分区间的长度。然后,求最大和最小值看是否满足条件。要在固定的区间内求最大最小值,就是著名的RMQ问题,这边可以用线段树解决,我这边用的是经典的ST(Space  Table)来解决。

         这边我们算一下复杂度。ST算法预处理需要O(nlogn)的复杂度,而每次询问需要O(1)的复杂度。枚举起点每次需要O(log n)的复杂度,要枚举n次,复杂度也是O(n log n),固总的复杂度为O(nlong)(如果用的是线段数来解决RMQ则复杂度为O(n *log n*log  n  )会多个log n 的时间,实际的话,需要 800多ms),cf上测试的时间为 400多ms.

代码:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const int maxk = 25;
const int maxn = 130000;

int n, k;
int h[maxn];
int min[maxn][maxk];
int max[maxn][maxk];

bool MIN(int a, int b){
    return (a < b);
}

bool MAX(int a, int b){
    return (a > b);
}

int getMin(int a, int b){
    if(a < b) return a;
    else return b;
}

int getMax(int a, int b){
    if(a > b) return a;
    else return b;
}

void build(int dp[][maxk],bool func(int,int)){
    int i, j, k;
    for(i = 1; i <= n; i++)
	dp[i][0] = i;
    for(k = 1,j = 1; k*2 <= n; k *= 2,j++)
	for(i = 1; i+k*2-1 <= n; i++)
	    if(func(h[dp[i][j-1]],h[dp[i+k][j-1]]))
		dp[i][j] = dp[i][j-1];
	    else
		dp[i][j] = dp[i+k][j-1];
}

int query(int l, int r){
    int len = r - l + 1;
    int maxValue, minValue;
    int t = (int)(log(len*1.0)/log(2.0));
    maxValue = getMax(h[max[l][t]],h[max[r + 1 - (1 << t)][t]]);
    minValue = getMin(h[min[l][t]],h[min[r + 1 - (1 << t)][t]]);
    return (maxValue - minValue);
}

int get(int i){
    int l, r;
    l = i, r = n + 1;
    while(l < r - 1){
	int m = (l + r) >> 1;
	if(query(i,m) <= k) l = m;
	else r = m;
    }
    return (l - i + 1);
}

int main(){
    int i, j;
    scanf("%d%d",&n,&k);
    for(i = 1; i <= n; i++)
	scanf("%d",h+i);

    build(min,MIN);
    build(max,MAX);

    int que[maxn];
    int now, top, ans = 0;
    for(i = 1; i <= n; i++){
	now = get(i);
	if(ans < now) {
	    ans = now;
	    top = 0;
	    que[top++] = i;
	}else if(ans == now){
	    que[top++] = i;
	}
    }
	
    printf("%d %d\n",ans,top);
    for(i = 0; i < top; i++)
	printf("%d %d\n",que[i],que[i]+ans-1);
    return 0;
}

       实际上,上述的算法,写得又长,效率又低。这题实际上有更好的方法

      方法二:

       如果我们知道以i开头的连续序列长度可以达到 len , 那么,以i+1开头的长度至少为 len - 1。那么我们可以在i,的基础上,来测试 以 i+1  开头的长度为 len的是否可以,这样一直做下去,直到找到 一个不符合条件的,那么此时序列的长度减一,就是以i开头可以得到的最大可以符合条件的最大长度。那么,这边就有一个问题了,我们如何知道一个区间的最大和最小值?我们可以先假定,我们只要求最大值(最小值可以类推)。我们可以用一个叫做单调队列的结构来储存求这个值。单调队列是这样做的,我们将一个新的数入队,如果发现现在队尾的元素没有比我们入队的大,那我们就可以把这个数出队。如果,我们发现最大的数就是现在 我元素i,那么我们在i做完处理后,我们就可以把i出队。之所以可以这样做是因为:如果队中的数比新加入的数大,那么它就不可能是以i,开头甚至以后的区间中,最大的元素(因为我们新入队的数永远比它大),也就是这个数是没有价值的,那么我们就可以抛弃它。如果最大的数是元素i,那么它对i+1及以后的结果是没影响的,所以我们也可以且必须抛弃它。

       这里我们算一下复杂度。由于在单调队列中,每个元素只入队一次,出队一次,固总的复杂度为O(n),已经达到理论的上界。实际运行中只用了80ms左右。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const int maxn = 130000;
int h[maxn], len[maxn];
int min[maxn], max[maxn];
int rank1[maxn], rank2[maxn];
int minl, minr, maxl, maxr;

int main(){
    int i, j, n, k;
    scanf("%d%d",&n,&k);
    for(i = 0; i < n; i++)
	scanf("%d",h+i);
    h[n] = h[n-1] + k + 1;

    minl = minr = 0;
    maxl = maxr = 0;
    rank1[minr] = rank2[maxr] = 0;
    min[minr++] = max[maxr++] = h[0];

    for(i = 0, j = 1; i < n; i++){
	for(;max[maxl] - min[minl] <= k;j++){
	    while(minl != minr && min[minr-1] >= h[j]) 
		minr--;
	    min[minr] = h[j],rank1[minr] = j;
	    minr += 1;

	    while(maxl != maxr && max[maxr-1] <= h[j]) 
		maxr--;
	    max[maxr] = h[j], rank2[maxr] = j;
	    maxr += 1;
	}

	len[i] = j - i - 1;
	if(rank1[minl] == i) minl++;
	if(rank2[maxl] == i) maxl++;
    }

    int ans = -1, num = 0;
    for(i = 0; i < n; i++)
	if(len[i] > ans){ 
	    num = 1;
	    ans = len[i];
	}else if(len[i] == ans){
	    num++;
	}

    printf("%d %d\n",ans,num);
    for(i = 0; i < n; i++)
	if(len[i] == ans)
	    printf("%d %d\n",i+1,i+len[i]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值