hdu2688 Rotate(树状数组)

Rotate

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2867    Accepted Submission(s): 633


Problem Description
Recently yifenfei face such a problem that give you millions of positive integers,tell how many pairs i and j that satisfy F[i] smaller than F[j] strictly when i is smaller than j strictly. i and j is the serial number in the interger sequence. Of course, the problem is not over, the initial interger sequence will change all the time. Changing format is like this [S E] (abs(E-S)<=1000) that mean between the S and E of the sequece will Rotate one times.
For example initial sequence is 1 2 3 4 5.
If changing format is [1 3], than the sequence will be 1 3 4 2 5 because the first sequence is base from 0.
 

Input
The input contains multiple test cases.
Each case first given a integer n standing the length of integer sequence (2<=n<=3000000) 
Second a line with n integers standing F[i](0<F[i]<=10000)
Third a line with one integer m (m < 10000)
Than m lines quiry, first give the type of quiry. A character C, if C is ‘R’ than give the changing format, if C equal to ‘Q’, just put the numbers of satisfy pairs.
 

Output
Output just according to said.
 

Sample Input
  
  
5 1 2 3 4 5 3 Q R 1 3 Q
 
Sample Output
  
  
10
8

题意:

给定一个长度为 n 的数组F[], 求数组的顺序对(即当 i < j 时, 满足F[i] < F[j](严格递增))的数量?在访问的同时,会对数组进行旋转操作(循环左移一位)。
数据说明:
2 <= n <= 3000000
m <= 10000
0 < F[i] <= 10000
操作
R S E 将指定区间(S+1, E)(abs(E - S)<= 1000)进行循环左移一位的操作
Q 询问当前状态下这个数组的顺序对

思路:

1.用树状数组(暴力,T(n) = O(n * m),显然超时)反向求出最初状态的顺序对, T(n) = O(n * lgn)
2.对于旋转操作:循环处理F[s + 1] ~ F[e]与F[s]的关系, 这是因为我们通过观察可以发现,每次移位只是把区间内的第一个数换到最后一个位置,其他直接进行移位,这样看来我们只需要考虑F[s]与其他位置的大小关系,如果是F[s] 较大,那么说明是他的是顺序对, 直接累加一次, 否则减一操作,每次移位T(n) = O(1000 * m)
3.对于询问操作:直接输出数量即可, T(n) = O(1)

注意:

这道题我的代码有些问题,原因至今不明。
1. 将__int64换位 long long 就超时,不是很懂, 都是64bits。
2.将操作数组str的大小改成非2的幂次就很容易超时.
据学长说这是因为编译器的问题。
大家找到真正的原因,请告知 !
某一博客原文:这题卡常数卡的有点紧,要注意常数优化,还有就是HDOJ的稳定性不是很好,同一个代码有时能过,有时不能过

AC代码:

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

using namespace std;

const int maxn = 1e4 + 10;
const int maxm = 3e6 + 10;

typedef __int64 LL;
LL C[maxn];
int ans[maxm];
inline int lowbit(int x)// 判断最右边1的位置
{
    return x & (-x);
}
void Add(int pos)// 更新c[i]的值
{
    while(pos < maxn)
    {
        C[pos] ++;
        pos += lowbit(pos);
    }
}
LL Sum(int pos)// 求和c[1]+...+c[x]
{
    LL sum = 0ll;
    while(pos > 0)
    {
        sum += C[pos];
        pos -= lowbit(pos);
    }
    return sum;
}
int main()
{
    int n, m;
    while(scanf("%d", &n) != EOF)
    {
        for(int i = 1; i < maxn; i ++) C[i] = 0ll;
        LL val = 0ll;
        for(int i = 0; i < n; i ++)
        {
            scanf("%d", &ans[i]);
            val += Sum(ans[i] - 1);
            Add(ans[i]);
        }
        scanf("%d", &m);
        while(m --)
        {
            char str[4];
            scanf("%s", str);
            if(str[0] == 'Q')
                printf("%I64d\n", val);
            else
            {
                int s, e;
                scanf("%d%d", &s, &e);
                if(s > e)
                {
                    int tmp = s;
                    s = e;
                    e = tmp;
                }
                int v = ans[s];
                for(int i = s; i < e; i ++)
                {
                    ans[i] = ans[i + 1];
                    if(v < ans[i + 1]) val --;
                    if(v > ans[i + 1]) val ++;
                }
                ans[e] = v;
            }
        }
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值