HDU 5289 Assignment(2015多校训练)

题目链接:戳我

题目大意:

公司有 n 个员工,编号为1....n,每个员工有能力 a[i],现将这些员工分为若干组,每组的员工的编号必须相连,且满足每组员工任意两个人能力的差值小于 k,可以一个人一组

问能分成多少组。

样例解释:

2       //两组测试样例

4 2      // 分别代表n, k

3 1 2 4 // 代表每个员工的能力,故可分组为[1,1]、[2,2]、[3,3]、[4,4] 、[2,3], 其中为数组的下标,其他均不符合题意,因为相差的值 >= 2

10 5 // 同上

0 3 4 5 2 1 6 7 8 9 // 同上

输出:

5

28

解题思路:

一道简单题。因为数据太大,输出的答案 变量 为 long long 型,而中间的变量没有用long long 型,导致中间变量计算溢出,从而导致答案错误,WA了好多次。

官方题解是这样说的:

解法一:枚举左端点,二分右短点,用st算法求区间最值

解法二:用单调队列维护最值。

本人解法:

维护一个区间的最大和最小值,,同时记录下最大值和最小值 的下标x、 y,当这个区间的最大和最小值 的 差 不满足 k 值时就跳出,得到这个区间的个数len,则有(len-1)*len/2种连续的组合,还要减去与上次重复的组合数。这时继续循环不是从 i+1开始,而是从min(x, y)+1开始。至于为什么要这么做的原因是显然的。

代码:

本人代码,358ms

//Author LJH
//www.cnblogs.com/tenlee
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#define clc(a, b) memset(a, b, sizeof(a))
using namespace std;

const int inf = 0x3f;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6+10;
long long a[maxn], n, k;

int main()
{
    freopen("1002.in", "r", stdin);
    int T, x;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n, &k);
        for(int i = 0; i < n; i++)
        {
            scanf("%lld", &a[i]);
        }

        long long ans = n;
        long long ma = a[0], mi = a[0], x, y;
        long long len = 1, i = 0, j;
        while(i < n-1)
        {
            x = y = i; //重新初始化
            mi = a[i];//重新初始化
            ma = a[i];//重新初始化
            len = 1;//重新初始化
            for(j = i+1; j < n; j++)
            {
                if(a[j] > ma)
                {
                    ma = a[j]; y = j;
                }
                if(a[j] < mi)
                {
                    mi = a[j]; x = j;
                }
                if(ma - mi < k)
                {
                    len++;
                }
                else
                {
                    break;
                }
            }
            ans += (len-1) * len / 2;
            if(j >= n) break;
            i = min(x, y) + 1;//下次循环从这里开始
            if(i < j)
            {
                ans -= (j-i) * (j-i-1) / 2; //因为可能会有重复的序列,故要减去
            }
        }
        printf("%lld
", ans);
    }
    return 0;
}

标程代码1201ms

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#define LL __int64
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
const int N = 200007;
int min[N][20];
int max[N][20];
int queryMin(int l,int r){
	int k=log2((double)(r-l+1));
	return Min(min[l][k],min[r-(1<<k)+1][k]);
}
int queryMax(int l,int r){
	int k=log2((double)(r-l+1));
	return Max(max[l][k],max[r-(1<<k)+1][k]);
}
int calc(int l,int r){
	int k=log2((double)(r-l+1));
	int MAX=Max(max[l][k],max[r-(1<<k)+1][k]);
	int MIN=Min(min[l][k],min[r-(1<<k)+1][k]);
	return MAX-MIN;
}
char filein[]={"9.in"};
char fileot[]={"9.out"};
int main(){
    //  freopen("data/data1002/1002.in", "r", stdin);
//	for(int t=0;t<10;++t){
//	filein[0]=t+'0';
//	fileot[0]=t+'0';
//	freopen(filein,"r",stdin);
//	freopen(fileot,"w",stdout);
	int T;
	int n,k,i,j,p;
	LL ans;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&k);
		for(i=1;i<=n;++i){
			scanf("%d",&j);
			min[i][0]=max[i][0]=j;
		}
		for(j=1;(1<<j)<=n;++j)
		for(i=1;i+(1<<j)-1<=n;++i){
			p=(1<<(j-1));
			min[i][j]=Min(min[i][j-1],min[i+p][j-1]);
			max[i][j]=Max(max[i][j-1],max[i+p][j-1]);
		}
		int l,r;
		ans=0;
		for(i=1;i<=n;++i){
			l=i,r=n;
			while(l+1<r){
				p=(l+r)>>1;
				if(calc(i,p)<k){
					l=p;
				}
				else{
					r=p;
				}
			}
			if(calc(i,r)<k){
				p=r;
				ans=ans+(LL)(r-i+1);
			}
			else{
				p=l;
				ans=ans+(LL)(l-i+1);
			}
		}
		printf("%I64d
",ans);
	}
//	fclose(stdin);
//	fclose(stdout);
//	}
	return 0;
}
原文地址:https://www.cnblogs.com/tenlee/p/4665464.html