BZOJ 1303 - [CQOI2009]中位数图 (技巧)

本文介绍了一道经典的算法题目——CQOI2009中位数图,并提供了解题思路及代码实现。通过对序列进行特殊转换,利用前缀和与标记数组来统计奇数长度连续子序列中位数为特定值的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1303: [CQOI2009]中位数图

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 1256   Solved: 813
[ Submit][ Status]

Description

给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。

Input

第一行为两个正整数n和b ,第二行为1~n 的排列。

Output

输出一个整数,即中位数为b的连续子序列个数。

Sample Input

7 4
5 7 2 4 3 1 6

Sample Output

4

HINT

第三个样例解释:{4}, {7,2,4}, {5,7,2,4,3}和{5,7,2,4,3,1,6}
N<=100000

Source

[ Submit][ Status]



之前没有想到,然后看到网上的可以把大于b的记为+1,小于b的记为-1,等于的记为0,然后一想就想到了

思考时间

。。。

。。。

。。。

。。。

。。。

。。。

。。。

。。。

。。。

。。。

通过上面的记法得到一个0,-1,1的整数序列A

这样就是A中有多少奇数长度连续子序列满足和为0

考虑前缀和S,连续子序列Aj..Ai和为sumij = Si-Sj-1,对于当前位置i有多少j满足sumij==0?

对上式移动一下就可以知道是Si==Sj-1的数量,因为-n<=Si<=n,所以一个标记数组就可以统计了



/**************************************************************
    Problem: 1303
    User: zhaosdfa
    Language: C++
    Result: Accepted
    Time:80 ms
    Memory:3616 kb
****************************************************************/
 
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#include <cmath>
#include <queue>
#include <set>
 
using namespace std;
 
//#define WIN
#ifdef WIN
typedef __int64 LL;
#define iform "%I64d"
#define oform "%I64d\n"
#define oform1 "%I64d"
#else
typedef long long LL;
#define iform "%lld"
#define oform "%lld\n"
#define oform1 "%lld"
#endif
 
#define S64I(a) scanf(iform, &(a))
#define P64I(a) printf(oform, (a))
#define P64I1(a) printf(oform1, (a))
#define REP(i, n) for(int (i)=0; (i)<n; (i)++)
#define REP1(i, n) for(int (i)=1; (i)<=(n); (i)++)
#define FOR(i, s, t) for(int (i)=(s); (i)<=(t); (i)++)
 
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const double PI = (4.0*atan(1.0));
 
const int maxn = 100000 + 20;
int A[maxn];
int sum[maxn];
int vis[maxn*2];
int vis1[maxn*2];
 
int main() {
    int n, b, ans = 0;
 
    scanf("%d%d", &n, &b);
    int G = n;
    sum[0] = 0;
    memset(vis, 0, sizeof(vis));
    memset(vis1, 0, sizeof(vis1));
    for(int i=1; i<=n; i++) {
        int t;
        scanf("%d", &t);
        if(t > b) A[i] = 1;
        else if(t == b) A[i] = 0;
        else A[i] = -1;
        sum[i] = sum[i-1] + A[i];
        int tn = sum[i];
        if(tn == 0 && (i&1)) ans++;
        if(i&1) ans += vis1[tn + G];
        else ans += vis[tn + G];
        if(i&1) vis[sum[i] + G]++;
        else vis1[sum[i] + G]++;
    }
    printf("%d\n", ans);
 
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值