[HDU 4907] 1001.Task schedule
中文题目,根据ti直接倒着预处理一次,就可以了。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <set>
#include <map>
#include <queue>
#include <stack>
#define INF 10
typedef long long LL;
using namespace std;
int a[200001], b[200002];
int main()
{
int t, n, m;
scanf("%d", &t);
while(t--)
{
memset(b, 0, sizeof(b));
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
b[a[i]] = 1;
}
for(int i = 200001; i >= 0; i--)
{
if(b[i] == 0)
{
flag = i;
}
a[i] = flag;
}
for(int i = 0; i < m; i++)
{
int temp;
scanf("%d", &temp);
printf("%d\n", a[temp]);
}
}
return 0;
}
题意是求中位数是M的一个子序列的个数。
看下数据规模,暴力是行不通的。比赛的时候没写完。。。
首先根据中位数的定义可知,当一个序列中,大于M的数字个数 等于 小于M的数字个数的时候,M一定是中位数。
也就是一个序列中,大于M的数字个数 - 小于M的数字个数 = 0,这个序列的中位数才能是M。
那我们就可以通过递推,分别统计M左右 大于M的数字个数 - 小于M的数字个数。
举个例子:
一个子序列,如果中位数是M,并且M左面如果 大于M的数字个数 - 小于M的数字个数 = 1,那么M右面 大于M的数字个数 - 小于M的数字个数 一定等于-1。
也就是左右的和保证平衡。
然后,用数组标记把左右的差值存起来,由于有负数,标记的时候需要把负数右移为正数进行标记。
我们枚举左右其中一面,就可以解决了,复杂度降低了。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <set>
#include <map>
#include <queue>
#include <stack>
#define INF 40000
typedef __int64 LL;
using namespace std;
int main()
{
int n, m, a[50000], b[100000], c[100000], d[100000], flag;
LL ans;
while(~scanf("%d %d", &n, &m))
{
ans = 0;
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
if(a[i] == m)
{
flag = i;
}
}
d[flag] = 0;
for(int i = flag-1; i >= 0; i--)
{
if(a[i] > a[flag])
{
d[i] = d[i+1] - 1;
} /*递推求M左面 大于M的数字个数-小于M的数字个数*/
else
{
d[i] = d[i+1] + 1;
}
++b[d[i]+INF]; //把左面 大于M的数字个数-小于M的数字个数 存到数组标记
}
for(int i = flag+1; i < n; i++)
{
if(a[i] > a[flag])
{
d[i] = d[i-1] - 1;
}
else //递推求M右面 大于M的数字个数-小于M的数字个数
{
d[i] = d[i-1] + 1;
}
++c[d[i]+INF]; //把M右面 大于M的数字个数-小于M的数字个数 存到数组标记
}
for(int i = 0; i < 80001; i++)
{
if(c[i] && b[INF+INF-i])
{
ans += (LL)c[i] * b[INF+INF-i];
}
}
printf("%I64d\n", ans + 1 + b[INF] + c[INF]);
}
return 0;
}