zoukankan      html  css  js  c++  java
  • 20190709 暑训 区间种类数 莫队的学习

    I - Turing Tree HDU - 3333 

    这个题目求的不是区间种类数,而是求一个区间不同数加和。

    这个题目第一次碰到感觉有点难,看了题解,就是首先对这个区间进行离散化,然后对于每一个查询区间对r进行排序。

    为什么要对 r 进行排序呢, 笼统的说就是消去前面更新对后面的影响。

    举个例子, 1 1 2 1 3  如果查询区间是 3 5, 1 4, 1 2 ,那么排完序之后就是  1 2,1 4,3 5

    查 1 2 的之前就可以把第一个1删去,第二个1 就放到第二个位置,查1 4 是不受影响的,查4 之前会把第三个和第四个放进去,然后第2个又会被删除,

    第四个又会放进去,。。。

    推荐博客  这个讲的很清楚。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <algorithm>
    #include <string>
    #include <stack>
    #include <iostream>
    #include <map>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 1e5 + 10;
    typedef long long ll;
    ll sum[maxn * 8];
    int a[maxn], b[maxn], vis[maxn];
    struct node
    {
        int l, r, id;
        ll ans;
    }ex[maxn];
    
    bool cmp1(node a,node b)
    {
        return a.r < b.r;
    }
    
    bool cmp2(node a,node b)
    {
        return a.id < b.id;
    }
    
    void push_up(int id)
    {
        sum[id] = sum[id << 1] + sum[id << 1 | 1];
        //printf("sum[%d]=%lld
    ", id, sum[id]);
    }
    
    void update(int id,int l,int r,int pos,int val)
    {
        if(l==r)
        {
            sum[id] = val;
        //    printf("  sum[%d]=%d
    ", id, val);
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) update(id << 1, l, mid, pos, val);
        else update(id << 1 | 1, mid + 1, r, pos, val);
        push_up(id);
    }
    
    ll query(int id,int l,int r,int x,int y)
    {
        if(x<=l&&y>=r) return sum[id];
        int mid = (l + r) >> 1;
        ll ans = 0;
        if (x <= mid) ans += query(id << 1, l, mid, x, y);
        if (y > mid) ans += query(id << 1 | 1, mid + 1, r, x, y);
        return ans;
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t--)
        {
            int n, m;
            scanf("%d", &n);
            memset(sum, 0, sizeof(sum));
            for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
            sort(b + 1, b + 1 + n);
            int len = unique(b + 1, b + 1 + n) - b - 1;
            for(int i=1;i<=n;i++)
            {
                a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;
            }
            scanf("%d", &m);
            for (int i = 1; i <= m; i++) scanf("%d%d", &ex[i].l, &ex[i].r), ex[i].id = i;
            sort(ex + 1, ex + 1 + m, cmp1);
            memset(vis, 0, sizeof(vis));
            int k = 1;
            for(int i=1;i<=n;i++)
            {
                if(vis[a[i]])
                {
                    update(1, 1, n, i, b[a[i]]);
                    update(1, 1, n, vis[a[i]], 0);
                    vis[a[i]] = i;
                }
                else
                {
                    update(1, 1, n, i, b[a[i]]);
                    vis[a[i]] = i;
                }
                //printf("a[%d]=%d b[%d]=%d
    ", i, a[i], a[i], b[a[i]]);
                while(i==ex[k].r&&k<=m)
                {
                    ex[k].ans = query(1, 1, n, ex[k].l, ex[k].r);
                    k++;
                }
            }
            sort(ex + 1, ex + 1 + m, cmp2);
            for (int i = 1; i <= m; i++) printf("%lld
    ", ex[i].ans);
        }
        return 0;
    }
    线段树

     

    区间种类数

    这个和上面那个很类似,基本上是一样的。

    这个可以用莫队来写,顺便学习了一下莫队。

    图灵数的莫队代码

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #include <vector>
    #include <map>
    #include <string>
    #include <iostream>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 1e5 + 10;
    typedef long long ll;
    int a[maxn], b[maxn], pos[maxn], vis[maxn];
    ll ans[maxn], Ans;
    struct node
    {
        int l, r, id;
    }ex[maxn];
    
    bool cmp(node a,node b)
    {
        return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
    }
    
    void add(int x)
    {
        if (!vis[a[x]]) Ans += b[a[x]];
        ++vis[a[x]];
    }
    
    void del(int x)
    {
        --vis[a[x]];
        if (!vis[a[x]]) Ans -= b[a[x]];
    }
    
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t--)
        {
            Ans = 0;
            memset(vis, 0, sizeof(vis));
            int n, m;
            scanf("%d", &n);
            int siz = sqrt(n);
            for (int i = 1; i <= n; i++) {
                scanf("%d", &a[i]);
                b[i] = a[i];
                pos[i] = i / siz;
            }
            sort(b + 1, b + n + 1);
            int len = unique(b + 1, b + 1 + n) - b - 1;
            for(int i=1;i<=n;i++)
            {
                a[i] = lower_bound(b + 1, b + len + 1, a[i]) - b;
            }
            scanf("%d", &m);
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d", &ex[i].l, &ex[i].r);
                ex[i].id = i;
            }
            sort(ex + 1, ex + 1 + m, cmp);
            int l = 1, r = 0;
            for(int i=1;i<=m;i++)
            {
                int ql = ex[i].l, qr = ex[i].r;
                while (l < ql) del(l), l++;
                while (l > ql) l--, add(l);
                while (r < qr) r++, add(r);
                while (r > qr) del(r), r--;
                ans[ex[i].id] = Ans;
            }
            for (int i = 1; i <= m; i++) printf("%lld
    ", ans[i]);
        }
        return 0;
    }
    莫队

    接下来写几个莫队的基本题目:

    E. XOR and Favorite Number  https://codeforces.com/contest/617/problem/E

    这个题目的意思是让你求一个区间的异或值等于k的数有多少,

    莫队基本上是套板子,只有add 和del 函数要自己写。

    这个异或首先我们可以求前缀异或和,sum[i]  然后因为 a^b=c 可得 a^c=b

    所以我们要一段区间 sum[i]^sum[j]=k  可以变成sum[i]^k=sum[j] 

    知道这一点了就好写多了,

    这个add  ans+=vis[a[i]^k]  ++vis[a[i]]

    这个del  ans-=vis[a[i]^k]    --vis[a[i]]

    这个上面都是看题解的,但是为什么这个题目可以用莫队写呢,首先这个不强制在线,

    其次觉得我们是要求一个区间满足条件的数,区间和满足条件的数的个数。

    这个一般就可以用到莫队,具体还是多写写题目,刷点感觉吧

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    #include <map>
    #include <string>
    #include <iostream>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 1e5 + 10;
    const int maxn1 = 1e7 + 10;
    typedef long long ll;
    int a[maxn], pos[maxn], vis[maxn1];
    ll ans[maxn], Ans;
    int n, m, k;
    int l, r;
    struct node
    {
        int l, r, id;
    }ex[maxn];
    
    bool cmp(node a,node b)
    {
        return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
    }
    
    void add(int x)
    {
        
        Ans += vis[a[x] ^ k];//这个先统计答案再加上这个,不重复加。这个顺序主要是解决a[x]^k=a[x],其他情况下先加哪个都一样。
        ++vis[a[x]];
        // printf("a[%x]=%d k=%d
    ", x, a[x], k);
        // printf("add l=%d r=%d x=%d Ans=%lld %d
    ",l,r, x, Ans,a[x]^k);
    }
    
    void del(int x)
    {
        --vis[a[x]];//先减去这个点,可以理解为求种类,先减去这个点,如果减去之后这个点不存在了,种类就-1
        Ans -= vis[a[x] ^ k];//这个同理,如果减去这个点之后,这个ans就要减去和这个成对的数,如果恰好是其本身
        //设未减去时为x,这个时候和他成对的应该只有x-1了。
        // printf("del l=%d r=%d x=%d Ans=%lld
    ",l,r ,x, Ans);
    }
    
    
    int main()
    {
        vis[0] = 1;
        scanf("%d%d%d", &n,&m,&k);
        int siz = sqrt(n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            a[i] ^= a[i - 1];
            pos[i] = i / siz;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d", &ex[i].l, &ex[i].r);
            ex[i].id = i;
        }
        sort(ex + 1, ex + 1 + m, cmp);
        l = 0, r = 0;
        for(int i=1;i<=m;i++)
        {
            int ql = ex[i].l, qr = ex[i].r;
            // printf("ql=%d qr=%d
    ", ql, qr);
            while (l <= ql) del(l), l++;
            while (l >= ql) l--, add(l);
            while (r < qr) r++, add(r);
            while (r > qr) del(r), r--;
            ans[ex[i].id] = Ans;
            // printf("Ans=%lld
    ", Ans);
        }
        for (int i = 1; i <= m; i++) printf("%lld
    ", ans[i]);
        return 0;
    }
    莫队

    BZOJ2038 

    注意开long long

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    #include <map>
    #include <string>
    #include <iostream>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 1e5 + 10;
    const int maxn1 = 1e7 + 10;
    typedef long long ll;
    int a[maxn], pos[maxn], vis[maxn];
    ll Ans;
    struct node
    {
        int l, r, id;
        ll ans;
    }ex[maxn];
    
    bool cmp(node a,node b)
    {
        return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
    }
    
    bool cmp1(node a,node b)
    {
        return a.id < b.id;
    }
    
    void add(int x)
    {
        Ans += vis[a[x]];
        ++vis[a[x]];
        //printf("add x=%d Ans=%d vis[%d]=%d
    ", x, Ans, a[x], vis[a[x]]);
    }
    
    void del(int x)
    {
        --vis[a[x]];
        Ans -= vis[a[x]];
        //printf("del x=%d Ans=%d vis[%d]=%d
    ", x, Ans, a[x], vis[a[x]]);
    }
    
    ll gcd(ll a,ll b)
    {
        return b == 0 ? a : gcd(b, a%b);
    }
    
    int main()
    {
        int n, m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            Ans = 0;
            memset(vis, 0, sizeof(vis));
            int sz = sqrt(n);
            for (int i = 1; i <= n; i++) {
                scanf("%d", &a[i]);
                pos[i] = i / sz;
            }
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d", &ex[i].l, &ex[i].r);
                ex[i].id = i;
            }
            sort(ex + 1, ex + 1 + m, cmp);
            int l = 1, r = 0;
            for(int i=1;i<=m;i++)
            {
                int ql = ex[i].l, qr = ex[i].r;
                while (l < ql) del(l), l++;
                while (l > ql) l--, add(l);
                while (r > qr) del(r), r--;
                while (r < qr) r++, add(r);
                ex[i].ans = Ans;
            }
            sort(ex + 1, ex + 1 + m, cmp1);
            for(int i=1;i<=m;i++)
            {
                ll len = ex[i].r - ex[i].l + 1;
                ll un = len * (len - 1) / 2;
                ll g = gcd(un, ex[i].ans);
                printf("%lld/%lld
    ", ex[i].ans / g, un / g);
            }
        }
        return 0;
    }
    莫队

    D. Powerful array

    这个题目也很好写,没什么思维量,如果你可以意识到这个是一个莫队了就应该可以很快的写出来。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    #include <map>
    #include <string>
    #include <iostream>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 1e6 + 10;
    const int maxn1 = 1e7 + 10;
    typedef long long ll;
    ll a[maxn], pos[maxn], vis[maxn1];
    struct node
    {
        int l, r, id;
    }ex[maxn];
    ll Ans, ans[maxn];
     
    bool cmp(node a,node b)
    {
        return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
    }
     
    void add(int x)
    {
    //    printf("x=%d
    ", x);
        Ans -= vis[a[x]] * vis[a[x]] * a[x];
        ++vis[a[x]];
        Ans += vis[a[x]] * vis[a[x]] * a[x];
    }
     
    void del(int x)
    {
        Ans -= vis[a[x]] * vis[a[x]] * a[x];
        --vis[a[x]];
        Ans += vis[a[x]] * vis[a[x]] * a[x];
    }
     
    int main() {
        int n, m;
        scanf("%d%d", &n, &m);
        int sz = sqrt(n);
        for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        for (int i = 1; i <= m; i++)
        {
            scanf("%d%d", &ex[i].l, &ex[i].r);
            pos[i] = i / sz;
            ex[i].id = i;
        }
        sort(ex + 1, ex + 1 + m, cmp);
        int l = 1, r = 0;
        for(int i=1;i<=m;i++)
        {
            int ql = ex[i].l, qr = ex[i].r;
            //printf("ql=%d qr=%d
    ", ql, qr);
            while (l < ql) del(l), l++;
            while (l > ql) l--, add(l);
            while (r < qr) r++, add(r);
            while (r > qr) del(r), r--;
            ans[ex[i].id] = Ans;
        }
        for (int i = 1; i <= m; i++) printf("%lld
    ", ans[i]);
        return 0;
    }
    莫队

    Lucky  HDU - 5213 

    这个题目没有之前那么简单,这个题,需要用到一点点的容斥定理来解决多区间询问问题。

    我们要找  从(l1,r1)找到一个x,再从(l2,r2)找到一个y,使得x+y==k 问这样的有多少对。

    这个是多区间的,莫队无法直接解决,所以就想办法转化成单区间的。

    从(l1,r1) 和 (l2,r2) 各找一个 相当于在 (l1,r2)中找到若干对,然后 减去 (l1,l2) 再减去 (r1,r2) 再加上 (r1,l2) 的

    这个被减去的都是不符合要求的。

    ans=f ( l1 , r2 ) - f( l1 , l2 ) - f ( r1 , r2 ) + f ( r1 , l2 )

    这个方法还挺巧妙的。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    #include <map>
    #include <string>
    #include <iostream>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 3e5 + 10;
    const int maxn1 = 1e7 + 10;
    typedef long long ll;
    ll a[maxn], pos[maxn];
    ll ans[maxn], Ans, vis[maxn];
    int n, k, m;
    struct node
    {
        int l,r, id;
        int f;
    }ex[maxn];
    
    bool cmp(node a,node b)
    {
        return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
    }
    
    void add(int x)
    {
        if (a[x] < k) Ans += vis[k - a[x]];
        ++vis[a[x]];
    }
    
    void del(int x)
    {
        --vis[a[x]];
        if (a[x] < k) Ans -= vis[k - a[x]];
    }
    
    
    int main()
    {
        while(scanf("%d%d",&n,&k)!=EOF)
        {
            Ans = 0;
            memset(vis, 0, sizeof(vis));
            memset(ans, 0, sizeof(ans));
            int sz = sqrt(n), tot = 0;
            for (int i = 1; i <= n; i++) {
                scanf("%lld", &a[i]);
                pos[i] = i / sz;
            }
            scanf("%d", &m);
            for(int i=1;i<=m;i++)
            {
                int l1, l2, r1, r2;
                scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
                ex[++tot].l = l1, ex[tot].r = r2, ex[tot].f = 1, ex[tot].id = i;
                ex[++tot].l = l1, ex[tot].r = l2 - 1, ex[tot].f = -1, ex[tot].id = i;
                ex[++tot].l = r1 + 1, ex[tot].r = r2, ex[tot].f = -1, ex[tot].id = i;
                ex[++tot].l = r1 + 1, ex[tot].r = l2 - 1, ex[tot].f = 1, ex[tot].id = i;
            }
            sort(ex + 1, ex + 1 + tot, cmp);
            int l = 0, r = 0;
            for(int i=1;i<=tot;i++)
            {
                int ql = ex[i].l, qr = ex[i].r;
                while (l < ql) del(l), l++;
                while (l > ql) l--, add(l);
                while (r < qr) r++, add(r);
                while (r > qr) del(r), r--;
                ans[ex[i].id] += Ans * 1ll * ex[i].f;
            }
            for (int i = 1; i <= m; i++) printf("%lld
    ", ans[i]);
        }
        return 0;
    }
    莫队

     

  • 相关阅读:
    班服 状压DP NOIP模拟赛
    记录奥林比克/课程录制 洛谷P2255 [USACO14JAN]
    字符串的展开 vijos1379 NOIP2007 字符串 模拟
    树网的核 Vijos1362 NOIP2007 树结构 直径 暴搜
    浏览器(Web Navigation) codevs 5373 POJ 1028 简单模拟
    FPS集合 Codgic1351 动态规划 DP NOIP模拟赛
    基于VBA的Excel抽奖软件
    【CodeForces】Round #436
    【NOI OpenJudge】【1.3】编程基础之算术表达式与顺序执行
    【NOI OpenJudge】【1.2】编程基础之变量定义、赋值及转换
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/11212350.html
Copyright © 2011-2022 走看看