hdu5919 Sequence II(主席树求区间数种数和k大查找)

题目链接

给你一个含有n个数的数列,m次查询。每次查询给出l和r两个数,表示子序列a[l]…a[r],然后子序列中有k种数(需要自己求出k值),
p[j]表示第j种数从左到右第一次出现的位置,那么会得到p[1]…p[k] && p[1] < p[2] < … < p[k]。然后倒着建立主席树,然后就类似区间k小的查找。
O(nlogn+nlogn+q(logn+logn))

#include <stdio.h>
#define min(x, y) x > y ? y : x;
#define max(x, y) x > y ? x : y;
int nCase = 0;
#define maxn  410000
int ls[maxn*20], rs[maxn*20], root[maxn];
int sum[maxn*20];
int tot;
void init() {
    tot = 0;
}
void build(int *rt, int l, int r) {
    *rt = ++tot;
    sum[*rt] = 0;
    if (l == r) return ;
    int mid = (l + r) >> 1;
    build(&ls[*rt], l, mid);
    build(&rs[*rt], mid + 1, r);
}
void updata(int last, int *rt, int l, int r, int pos, int val) {
    *rt = ++tot;
    sum[*rt] = sum[last] + val;
    ls[*rt] = ls[last];
    rs[*rt] = rs[last];
    if (l == r) return ;
    int mid = (l + r) >> 1;
    if (pos <= mid) updata(ls[last], &ls[*rt], l, mid, pos, val);
    else updata(rs[last], &rs[*rt], mid + 1, r, pos, val);
}
int res;
int find(int rt, int l, int r, int pos) {
    res = 0;
    int mid;
    while(l <= r) {
        mid = (l + r) >> 1;
        if (r == pos) {res += sum[rt];break;}
        if (mid == pos) {
            res += sum[ls[rt]];
            break;
        }
        if (pos <= mid) {
            rt = ls[rt];
            r = mid;
        }else {
            res += sum[ls[rt]];
            rt = rs[rt];
            l = mid + 1;
        }
    }
    return res;
}
int get(int rt, int l, int r, int pos, int k) {
    int mid;
    while(l < r) {
        mid = (l + r) >> 1;
        if (pos <= mid) {
            rt = ls[rt];
            r = mid;
        }else {
            if (sum[ls[rt]] >= k) {
                rt = ls[rt];
                r = mid;
            }else {
                k -= sum[ls[rt]];
                rt = rs[rt];
                l = mid + 1;
            }
        }
    }
    return l;
}
int a[maxn];
int ans[maxn];
int mp[maxn];
int kase;
int l, r, ll, rr, k, low, high, p;
int n, q, i;
int main(int argc, const char * argv[])
{
    scanf("%d", &kase);
    while(kase--) {
        scanf("%d%d", &n, &q);
        for (i = 1;i <= n;++i){ scanf("%d", &a[i]);mp[a[i]] = 0;}
        init();
        build(&root[n + 1], 1, n);
        for (i = n;i >= 1;--i) {
            if (mp[a[i]] == 0) updata(root[i + 1], &root[i], 1, n, i, 1);
            else {
                int temp;
                updata(root[i + 1], &temp, 1, n, mp[a[i]], -1);
                updata(temp, &root[i], 1, n, i, 1);
            }
            mp[a[i]] = i;
        }
        printf("Case #%d:", ++nCase);
        ans[0] = 0;
        for (i = 1;i <= q;++i) {
            scanf("%d%d", &l, &r);
            ll = min((l + ans[i - 1]) % n + 1, (r + ans[i - 1]) % n + 1);
            rr = max((l + ans[i - 1]) % n + 1, (r + ans[i - 1]) % n + 1);
            l = ll, r = rr;
            k = find(root[l], 1, n, r);
            if (k & 1) k = k / 2 + 1;
            else k = k / 2;
            p = get(root[l], 1, n, r, k);/*类似k小的查找*/
            /*也可以二分求p值*/
            ans[i] = p;
            printf(" %d", p);
        }
        puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于 HDU4992 所有原根的问题,我可以给出以下解答: 1. 首先,我们需要知道什么是原根。原根是指模 n 意义下,所有与 n 互质的 a 的最小正整 k 次幂对 n 取模的值覆盖了所有与 n 互质的。简单来说,就是如果一个 a 是模 n 意义下的原根,那么 a 的任何正整次幂对 n 取模的值都不相同,且能覆盖所有与 n 互质的。 2. 为了模 n 意义下的所有原根,我们需要先出与 n 互质的的个 phi(n)。phi(n) 可以使用欧拉函出。 3. 然后,我们需要枚举模 n 意义下的所有,判断它是否是原根。具体来说,对于每个 a,我们需要判断 a 的每个小于 phi(n) 的正整次幂对 n 取模的值是否都不相同,且能覆盖所有与 n 互质的。如果是,那么 a 就是模 n 意义下的原根。 4. 代码实现可以参考以下 Java 代码: ``` import java.util.*; public class Main { static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } static int phi(int n) { int res = n; for (int i = 2; i * i <= n; i++) { if (n % i == 0) { res = res / i * (i - 1); while (n % i == 0) { n /= i; } } } if (n > 1) { res = res / n * (n - 1); } return res; } static int pow(int a, int b, int mod) { int res = 1; while (b > 0) { if ((b & 1) != 0) { res = res * a % mod; } a = a * a % mod; b >>= 1; } return res; } static boolean check(int a, int n, int phi) { for (int i = 1, j = pow(a, i, n); i <= phi; i++, j = j * a % n) { if (j == 1) { return false; } } return true; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt(); int phi = phi(n); List<Integer> ans = new ArrayList<>(); for (int i = 1; i < n; i++) { if (gcd(i, n) == 1 && check(i, n, phi)) { ans.add(i); } } Collections.sort(ans); for (int x : ans) { System.out.print(x + " "); } System.out.println(); } } } ``` 其中,gcd 函用于最大公约,phi 函用于欧拉函,pow 函用于快速幂模,check 函用于判断一个是否是原根。在主函中,我们依次读入每个 n,出 phi(n),然后枚举模 n 意义下的所有,判断它是否是原根,将所有原根存入一个 List 中,最后排序输出即可。 希望我的回答能够帮到你,如果你有任何问题,欢迎随时提出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值