2013 ACMICPC Hangzhou Rabbit Kingdom

Rabbit Kingdom

题目描述

给一个N个数的序列,M个询问,每个询问两个数L , R 。 问 [L , R] 中有多少个数和区间中所有数(除了自己都互质)

解法

首先肯定要O(N * sqrt(N)) 预处理出每个数互质的话左右分别能延伸到什么地方,记为l[i] , r[i].
接下来就有两种解法了

Solution Of cxlove 

爱酱的解法应该是正解,用一个BIT来维护哪些数是可以的。
将所有的 l[i] 位置用边表插入 i ,以备后面之用
首先把询问按照 l 离线,然后将 l[i] < 1(也就是能左边撑到头)的数字 在 i 的位置 + 1 , 如果 r[i] <= n 那么 r[i]  的位置 -1 。 那么前缀和就是这个数字能够覆盖到的位置。
考虑一个区间左指针,当询问改变的时候,区间左指针 left 右移,同时把当前的数字恢复——即 left 位置 -1 , 如果 r[left] <= n 那么 r[left] 位置 +1。
这时候,我们就要用上那个边表了,对于l[i] 在 left 的数字,那么在 left 之后都是可以的,所以我们遍历所有 left 位置的边表,设为 pos[left][j] 是他的 id 。 那么 pos[left][j]
 这个位置就是可以的,我们 + 1 , 同时一样,他的右边位置就要 -1 ( r[pos[left][j]] ) 。
这样我们在扫到询问位置 q[i].l 的时候,只要查询 l , r 之间有多少个 数字是可以的就好,那么就是 sum (q[i].r) - sum (q[i].l - 1); 。 这个问题就被解决了

code

typedef long long LL;
const int N = 200005;
template<class T> inline T& RD(T &x){
    //cin >> x;
    //scanf("%d", &x);
    char c; for (c = getchar(); c < '0'; c = getchar()); x = c - '0'; for (c = getchar(); '0' <= c && c <= '9'; c = getchar()) x = x * 10 + c - '0';
    //char c; c = getchar(); x = c - '0'; for (c = getchar(); c >= '0'; c = getchar()) x = x * 10 + c - '0';
    return x;
}
struct Query {
    int l , r , id;
    void input (int i) {
        id = i;
        RD(l); RD(r);
    }
    bool operator < (const Query &q) const {
        return l < q.l;
    }
}q[N];
int n , m , a[N] , s[N];
int flag[N] , prime[N] , cnt , minfac[N];
int fac[N][20] , p[N] , l[N] , r[N];
vector <int> pos[N];
void Init () {
    for (int i = 2 ; i < N ; i ++) {
        if (!flag[i]) {
            prime[cnt ++] = i;
            minfac[i] = i;
        }
        for (int j = 0 ; j < cnt && prime[j] * i < N ; j ++) {
            flag[i * prime[j]] = 1;
            minfac[i * prime[j]] = prime[j];
            if (i % prime[j] == 0) break;
        }
    }
    // minfac[1] = 1;
    // fac[1][0] = 1;fac[1][1] = 1;
    for (int i = 2 ; i < N ; i ++) {
        fac[i][0] = 0;
        int m = i;
        while (m != 1) {
            fac[i][++ fac[i][0]] = minfac[m];
            m /= minfac[m];
        }
    }
}
void add (int idx , int v) {
    // cout << "update : " << idx << " " << v << endl;
    for (int i = idx ; i <= n ; i += lowbit (i))
        s[i] += v;
}
int sum (int idx) {
    int ret = 0;
    for (int i = idx ; i > 0 ; i -= lowbit (i))
        ret += s[i];
    return ret;
}
int ans[N];
int main () {
    #ifndef ONLINE_JUDGE
        freopen ("input.txt" , "r" , stdin);
        // freopen ("output.txt" , "w" , stdout);
    #endif
    Init ();
    while (RD(n) , RD(m) , n + m) {
        for (int i = 1 ; i <= n ; i ++)
            RD(a[i]);
        for (int i = 0 ; i < N ; i ++)
            pos[i].clear ();
        memset (p , 0 , sizeof(p));
        for (int i = 1 ; i <= n ; i ++) {
            int idx = 0;
            for (int j = 1 ; j <= fac[a[i]][0] ; j ++)
                idx = max (idx , p[fac[a[i]][j]]);
            for (int j = 1 ; j <= fac[a[i]][0] ; j ++)
                p[fac[a[i]][j]] = i;
            l[i] = idx;
            pos[idx].push_back (i);
        }
        memset (p , 0x11 , sizeof(p));
        for (int i = n ; i > 0 ; i --) {
            int idx = n + 1;
            for (int j = 1 ; j <= fac[a[i]][0] ; j ++)
                idx = min (idx , p[fac[a[i]][j]]);
            for (int j = 1 ; j <= fac[a[i]][0] ; j ++)
                p[fac[a[i]][j]] = i;
            r[i] = idx;
        }
        for (int i = 0 ; i < m ; i ++)
            q[i].input (i);
        sort (q , q + m);
        memset (s , 0 , sizeof(s));
        for (int i = 1 ; i <= n ; i ++) {
            if (l[i] < 1) {
                add (i , 1);
                if (r[i] <= n)
                    add (r[i] , -1);
            }
        }
        int left = 1;
        for (int i = 0 ; i < m ; i ++) {
            while (left < q[i].l) {
                add (left , -1);
                if (r[left] <= n) add (r[left] , 1);
                for (int j = 0 ; j < pos[left].size() ; j ++) {
                    add (pos[left][j] , 1);
                    if (r[pos[left][j]] <= n) add (r[pos[left][j]] , -1);
                }
                left ++;
            }
            ans[q[i].id] = sum (q[i].r) - sum (q[i].l - 1);
            // cout << q[i].id << " " << ans[q[i].id] << " " << left << endl;
        }
        for (int i = 0 ; i < m ; i ++)
            printf ("%d\n" , ans[i]);
    }
    return 0;
}


Solution Of Dshawn

我的方法比较麻烦,按照 r 将询问排序。
用两个 BIT , 维护可行的左区间位置数(也就是说我能要求右区间一定满足!!)
首先还是有一个左指针 first , 当左指针 <= query[i].r 的时候一直右滑,与此同时:
1、那么对于 first 位置的这个数,L[first] 就是一个成功的区间,那么要 L[first] + 1;
2、我用边表在 R[first] 的位置 记录他的左指针位置和他自己的位置 addR(R[first] , L[first] , first);
3、对于右区间在 first 位置的区间肯定走远了,于是我们遍历边表,找到所有R[X] = first 的区间,删掉,那么就是将对应的 L[X] - 1 , 并且我们记录删掉了 X , 也是用BIT 在 X的位置 +1.那么通过这一步我们就把所有右区间 <= query[i].r 的都删掉了。
对于询问,答案肯定等于 query[i].r - query[i].l + 1(总共的元素个数) - 右区间不符合的、我们删掉的个数 - 左区间不符合的个数。要注意,左右区间都不符合的已经包含在右区间不符合的个数中了,我们不就记录在左区间不符合的个数内。
那么通过这个简单的容斥问题就解决了。

code

int n , m;
const int N = 4e5 + 9;
using namespace Math;
struct Query{
    int l , r , id;
    bool operator < (const Query & A) const{
        return r < A.r;
    }
}query[N];
int W[N];
int L[N] , R[N];
int last[N];
int tree[N];
int deltree[N];
void delOne(int x){
    for ( ; x < N ; x += low_bit(x))
        deltree[x]++;
}
int querydel(int x){
    int ret = 0;
    for ( ; x ; x -= low_bit(x))
        ret += deltree[x];
    return ret;
}
int head[N];
struct Node{
    int next;
    int r;
    int id;
}node[N];
int tot;
void addR(int x , int r , int id){
    node[tot].next = head[x];
    node[tot].r = r;
    node[tot].id = id;
    head[x] = tot++;
}
void add(int x , int y){
    for ( ; x < N ; x += low_bit(x))
        tree[x] += y;
}
int getsum(int x){
    int ret = 0;
    for ( ; x ; x -= low_bit(x))
        ret += tree[x];
    return ret;
}
int ans[N] , stand[N];
void debug(){
    for (int i = 0 ; i < m ; ++i){
        int ans = 0;
        for (int j = query[i].l ; j <= query[i].r ; ++j){
            bool ok = true;
            for (int k = query[i].l ; k <= query[i].r ; ++k)
                ok &= j == k || __gcd(W[j] , W[k]) == 1;
            ans += ok;
        }
        stand[i] = ans;
    }
}
void solve(){
    n++;
    for (int i = 2 ; i <= n ; ++i) {
            RD(W[i]);
//            W[i] = rand() % (int)2e5 + 1;
//            printf("%d " , W[i]);
    }
//    puts("");
    for (int i = 0 ; i < m ; ++i){
//        scanf("%d%d" , &query[i].l , &query[i].r);
        RD(query[i].l , query[i].r);
//        query[i].l = rand() % (n - 1) + 1;
//        query[i].r = query[i].l + rand() % (n - query[i].l);
//        printf("Query %d %d\n" , query[i].l , query[i].r);
        query[i].l++;query[i].r++;
        query[i].id = i;
    }
//    debug();
    sort(query , query + m);
    for (int i = 1 ; i < N ; ++i) last[i] = 1;
    for (int i = 2 ; i <= n ; ++i){
        getFactors(W[i]);
        L[i] = 1;
        for (int j = 0 ; j < facCnt ; ++j){
            checkMax(L[i] , last[factor[j][0]]);
            last[factor[j][0]] = i;
        }
    }
    for (int i = 1 ; i < N ; ++i) last[i] = n + 1;
    for (int i = n ; i >= 2 ; --i){
        getFactors(W[i]);
        R[i] = n + 1;
//        cout << "factor ";
        for (int j = 0 ; j < facCnt ; ++j){
//                cout << factor[j][0] << ' ';
            checkMin(R[i] ,  last[factor[j][0]]);
            last[factor[j][0]] = i;
        }
//        cout << endl;
    }
//    for (int i = 2 ; i <= n ; ++i) printf("[%d,%d]\n" , L[i] , R[i]);
//    for (int i = 2 ; i <= n ; ++i)
//        for (int j = i + 1 ; j <= n ; ++j)
//            printf("%d %d %d\n" , i , j , __gcd(W[i] , W[j]));
    RST(tree);
    RST(deltree);
    int first = 2;
    tot = 0;
    FLC(head , -1);
    for (int i = 0 ; i < m ; ++i){
        while(first <= query[i].r){
            add(L[first] , 1);
            addR(R[first] , L[first] , first);
            for (int j = head[first] ; j != -1 ; j = node[j].next){
                add(node[j].r , -1);
                delOne(node[j].id);
            }
            first++;
        }
        ans[query[i].id] = query[i].r - query[i].l + 1 - (getsum(query[i].r) - getsum(query[i].l - 1) + querydel(query[i].r) - querydel(query[i].l - 1));
    }
    for (int i = 0 ; i < m ; ++i) {
            printf("%d\n" , ans[i]);
//            if (ans[i] != stand[i]) {
//                    cout << "^^^" << endl;
//                    system("pause");
//            }
    }
}
int main(){
//    srand(time(0));
    getPrime();
    while(RD(n , m) , (n || m)) solve();
}




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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

ACM的记忆

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值