HDU 5042 分段乱搞

【题目大意】

给一个有n个元素数组a[]。定义F(l,r) == gcd(ai);1 <= l <= r <= n。

给了一个计算RANK(l,r)的函数,返回值是一个pair,(k1,k2)

两种询问,1、已知l,r,求k1,k2;2、已知k1,k2,求l,r。

【思路】

若l固定,r从l增大到n。其F()值会越来越小,而且,后者一定会是前者的一个因子。所以l固定,F()值的种类最多分为log(n-l)种。这就是解题的关键。

想到分段了后,这题其实就不是很难了,感觉会有各种各样的解法,就是有点麻烦。下面说我怎么做的。

如果l固定,我们将F()分段。这里对应代码中的node结构体,一个node的意思是(l,r1...r2)的F()值都为g。

我们按g值相同的段,放到一起。排序第一关键字为l,第二为r2(r1也行)。

(一)已知l,r,求k1,k2。这里先要求F(l,r),这里有多种方法,随便搞搞就能解出来了。如果F(l,r)== g ,k1等于F()值小于g值的种类+1,这也是很好求的。又,因为我们把g值相同的段放在一起,利用二分,很容易求出来k2。

(二)已知k1,k2,,求l,r。利用二分,很容易得到答案的g值(即F())。g值确定了,答案(l,r)其实就是这一类的第k2个区间。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef long long LL;
typedef unsigned long long ULL;
/* ****************** */
const int INF = 100011122;
const double INFF = 1e100;
const double eps = 1e-8;
const LL mod = 20140518;
const int NN = 100010;
const int MM = 5000010;
/* ****************** */

struct node1
{
    int r, g;
    node1(int a = 0,int b = 0):r(a), g(b){}
    bool operator<(const node1 &tt)const
    {
        return r < tt.r;
    }
    bool operator==(const node1 &tt)const
    {
        return (r == tt.r);
    }
};
vector<node1>bb[NN];
struct node
{
    int l,r1,r2,g;
    node(int a = 0,int b = 0,int c = 0,int d = 0): l(a), r1(b), r2(c), g(d){}
    bool operator<(const node &tt)const
    {
        if(l == tt.l)
        {
            return r2 < tt.r2;
        }
        return l < tt.l;
    }
    bool operator==(const node &tt)const
    {
        return (l == tt.l) && (r2 == tt.r2);
    }
};
vector<node> aa[NN];
vector<LL> ge[NN];
node sta[NN];
LL sum[NN];
int a[NN];

int gcd(int a,int b)
{
    if(b == 0) return a;
    return gcd(b, a%b);
}

void solve(int n,int m,int max_g)
{
    char op[15];
    LL k1, k2;
    int g, l, r, temp;
    while(m--)
    {
        scanf("%s", op);
        if(op[0] == 'R')
        {
            scanf("%d%d",&l, &r);
            g = lower_bound(bb[l].begin(), bb[l].end(), node1(r, 0)) -> g;

            k1 = sum[g-1];

            temp = upper_bound(aa[g].begin(), aa[g].end(), node(l, l, r, 0)) - aa[g].begin();
            if(temp == 0)
                k2 = 0;
            else
                k2 = ge[g][temp-1];
            if(temp < (int)ge[g].size() && l == aa[g][temp].l && r >= aa[g][temp].r1)
            {
                k2 += r - aa[g][temp].r1 + 1;
            }

            k1++;
            printf("%I64d %I64d\n",k1, k2);
        }
        else
        {
            scanf("%I64d%I64d",&k1, &k2);
            k1--;

            g = upper_bound(sum+1, sum+1+max_g, k1) - sum;

            if(g > max_g || sum[g-1] != k1 || ge[g][ ge[g].size() - 1 ] < k2)
            {
                puts("-1");
                continue;
            }

            temp = upper_bound(ge[g].begin(), ge[g].end(), k2-1) - ge[g].begin();
            if(temp > 0)
                k2 -= ge[g][temp-1];

            l = aa[g][temp].l;
            r = aa[g][temp].r1 + k2 - 1;
            printf("%d %d\n",l, r);
        }
    }
}

int main()
{
    int cas, ee = 0;
    int n, m, i, j, tail, tol, temp;
    int max_g;
    bool fg;
    LL pre;
    scanf("%d", &cas);
    while(cas--)
    {
        scanf("%d%d", &n, &m);
        for(i = 1; i <= n; i ++)
            scanf("%d", &a[i]);


        max_g = 0;
        tail = 0;
        for(i = n; i >= 1; i --)
        {
            sta[++tail] = node(i, i, i, a[i]);
            for(j = tail - 1; j >= 1; j --)
            {
                sta[j].l --;
                sta[j].g = gcd(sta[j].g, a[i]);
            }
            tol = 1;
            for(j = 2; j <= tail; j ++)
            {
                if(sta[j].g == sta[tol].g)
                {
                    sta[tol].r1 = sta[j].r1;
                }
                else
                {
                    sta[++tol] = sta[j];
                }
            }
            tail = tol;
            for(j = 1; j <= tail; j ++)
            {
                max_g = max(max_g, sta[j].g);
                aa[ sta[j].g ].PB( sta[j] );
            }
            for(j = tail; j >= 1; j --)
            {
                bb[i].PB( node1(sta[j].r2, sta[j].g) );
            }
        }

        sum[0] = 0;
        for(i = 1; i <= max_g; i ++)
        {
            tol = aa[i].size();
            sum[i] = sum[i-1];
            for(j = 0; j < tol/2; j ++)
            {
                swap(aa[i][j], aa[i][tol-1-j]);
            }
            pre = 0;
            fg = false;
            for(j = 0; j < tol; j ++)
            {
                temp = aa[i][j].r2 - aa[i][j].r1 + 1;
                if(temp)
                    fg = true;

                ge[i].PB(temp + pre);
                pre += temp;
            }
            if(fg) sum[i]++;
        }

        printf("Case #%d:\n", ++ee);
        solve(n, m, max_g);

        for(i = 1; i <= max_g; i ++)
        {
            aa[i].clear();
            ge[i].clear();
        }
        for(i = 1; i <= n; i ++)
        {
            bb[i].clear();
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值