9.25 Test —— 数据结构练习-hard

T1:abnormal

不正常团伙

题目描述

  有N个人站成一行, 每个人有一个魅力值, 相同魅力值得人会形成一个团伙, 你出于对于社会和谐发展得考虑, 定义一个团伙正常当且仅当团伙人数为2, 现在你的任务就是回答M个询问,每次询问一个区间[L, R], 你需要回答这个区间中所有人各自结成团伙后,处于不正常团伙中的人的魅力值之和。

输入格式

  第一行两个数N, M, 分别表示人数与询问数。

  接下来一行N个数, 分别表示每个人的魅力值。

  接下来M行每行两个数L, R, 表示一个询问。

 

输出格式

  对于每一个询问,输出一行表示答案。

样例输入

5 5

4 3 3 4 1

1 1

1 4

1 2

1 5

2 3

样例输出

4

0

7

1

0

数据范围

  对于20%数据, 满足 n <= 500, m <= 500

  对于50%数据, 满足n <= 10000, m <= 10000

  对于100%数据, 满足n <= 105, m <= 105, ai <= 105

解析:

  题解是线段树做法,而我是打的莫队

   莫队维护很简单,就不用多说了

 代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005;

inline int read()
{
    int ret, f=1;
    char c;
    while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1;
    ret=c-'0';
    while((c=getchar())&&(c>='0'&&c<='9'))ret=(ret<<3)+(ret<<1)+c-'0';
    return ret*f;
}

int siz, n, m, pos[maxn], a[maxn], t[maxn], b[maxn], bnum;
ll ans[maxn];

struct ques{
    int l, r, num;
}q[maxn];

bool cmp(ques x, ques y)
{
    return pos[x.l] != pos[y.l]? pos[x.l] < pos[y.l]: x.r < y.r;
}

void work()
{
    int L, R;    
    ll now = 0LL;    
    for(int i = 1; i <= m; ++i)
    {
        if(pos[q[i].l] != pos[q[i-1].l])
        {
            for(int j = 1; j <= bnum; ++j)
                t[j] = 0;
            now = 0LL;
            R = q[i].l - 1;
            L = q[i].l;
        }
        while(R < q[i].r)
        {
            t[a[R+1]] ++;
            if(t[a[R+1]] == 2)
                now -= b[a[R+1]];
            else if(t[a[R+1]] == 3)
                now += 3LL * b[a[R+1]];
            else
                now += (ll)b[a[R+1]];
            R ++;
        }
        while(L > q[i].l)
        {
            t[a[L-1]] ++;
            if(t[a[L-1]] == 2)
                now -= b[a[L-1]];
            else if(t[a[L-1]] == 3)
                now += 3LL * b[a[L-1]];
            else
                now += (ll)b[a[L-1]];
            L --;
        }
        while(L < q[i].l)
        {
            t[a[L]] --;
            if(t[a[L]] == 2)
                now -= 3LL * b[a[L]];
            else if(t[a[L]] == 1)
                now += (ll)b[a[L]];
            else 
                now -= (ll)b[a[L]];
            L ++;
        }
        ans[q[i].num] = now;    
    }
}

int main()
{
    freopen("abnormal.in", "r", stdin);
    freopen("abnormal.out", "w", stdout);
    n = read(); m = read();
    for(int i = 1; i <= n; ++i)
        b[i] = a[i] = read();
    for(int i = 1; i <= m; ++i)
    {
        q[i].l = read();
        q[i].r = read();
        q[i].num = i;
    }
    sort(b + 1, b + n + 1);
    bnum = unique(b + 1, b + n + 1) - b - 1;
    siz = sqrt(1.0 * n + 0.5);
    for(int i = 1; i <= n; ++i)
    {
        pos[i] = (i - 1) / siz + 1;
        a[i] = lower_bound(b + 1, b + bnum + 1, a[i]) - b;
    }
    sort(q + 1, q + m + 1, cmp);
    work();
    for(int i = 1; i <= m; ++i)
        printf("%lld\n", ans[i]);
    return 0;
}
abnormal

T2:irregular

不正常国家

题目描述

  有一个长相惊奇的国家......

  可以猜到, 这个国家有N个城市, 每个城市之间有且仅有一条通路。

  可以猜到, 这个国家是长在树上的。

  可以猜到, 首都是1号节点。

  可以猜到, 每个城市管辖他子树中的所有城市。

  可以猜到, 每个城市有一个权值Ai, 两个城市的通讯难度为两城市路径异或和。

  可以猜到, 一个城市的繁忙度定义为它所管辖的城市中通讯难度最大的两个城市的通讯难度。

  可以猜到, 还有一点特别需要注意, 如果两个城市(x, y)都是在a的子树中, 但是lca(x, y) != a, 那么这两个城市不参与a繁忙度的统计

  可以猜到, 这道题你只需要输出所有城市的繁忙度即可。

  可以猜到, 这道题是水题......

输入格式

  第一行一个数N。

  第二行N个数, 表示点权Ai

  接下来N-1个数, 表示树上的一条边, 默认1号节点为根

输出格式

  1行N个数, 表示每个节点的子树中异或和最大的路径异或和

样例输入

5

1775 6503 8147 2354 8484

1 2

1 3

3 4

1 5

 

数据范围

  对于30%数据, n <= 100.

  对于100%数据, n <= 100000.

解析:

   可以猜到, 这道题与trie有关

  其实还是很容易联想到树上最大异或路径的那道题

  但这道题需要求每一个子树中的最大异或路径,显然不能每一次都暴力重构trie

  其实父亲的trie是可以从儿子那里继承过来的

  按照套路,设点$x$到根的异或和为$xr[x]$, 则任意两点间的异或和 $len = xr[x] \wedge xr[y] \wedge a[lca(x, y)]$

  显然枚举lca, 子树查询信息, 可以选择一个儿子节点继承信息

  这不就是DSU ON TREE 吗?

  考试时口胡了trie + DSU ON TREE 的做法, 但从没打过DSU ON TREE , 所以也只是说说罢了,最后我连暴力都没打

 代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005;

int a[maxn], xr[maxn], n, siz[maxn], root[maxn], son[maxn], ans[maxn];

int head[maxn], tot;
struct edge{
    int nxt, to;
}e[maxn<<1];

void Addedge(int x, int y)
{
    e[++tot] = (edge){head[x], y};
    head[x] = tot;
}

void dfs1(int x, int fa)
{
    siz[x] = 1;
    for(int i = head[x]; i; i = e[i].nxt)
    {
        int id = e[i].to;
        if(id == fa)    continue;
        xr[id] = xr[x] ^ a[id];
        dfs1(id, x);
        siz[x] += siz[id];
        if(siz[id] > siz[son[x]])
            son[x] = id;
    }
}

int go[maxn*32][2], ndnum, tra[maxn*32], topt;

void Add(int now, int x)
{
    for(int i = 30; i >= 0; --i)
    {
        int to = ((x >> i) & 1);
        if(!go[now][to])    go[now][to] = topt? tra[topt--]: ++ ndnum;
        now = go[now][to];
    }
}

int Query(int now, int x, int val)
{
    if(!go[now][0] && !go[now][1])    return 0;
    int ret = 0;
    for(int i = 30; i >= 0; --i)
    {
        int k = (((a[x] >> i) & 1) ^ ((val >> i) & 1)); 
        if(go[now][k^1])
        {
            ret |= (1 << i);
            now = go[now][k^1];
        }
        else
            now = go[now][k];
    }
    return ret;
}

void update(int id, int x, int fa)
{
    ans[id] = max(ans[id], Query(root[id], id, xr[x]));
    for(int i = head[x]; i; i = e[i].nxt)
    {
        int to = e[i].to;
        if(to == fa)    continue;
        update(id, to, x);
    }
}

void Union(int id, int x, int fa)
{
    Add(root[id], xr[x]);
    for(int i = head[x]; i; i = e[i].nxt)
    {
        int to = e[i].to;
        if(to == fa)    continue;
        Union(id, to, x);
    }
}

void rem(int x)
{
    if(go[x][0])    rem(go[x][0]);
    if(go[x][1])    rem(go[x][1]);
    tra[++topt] = x;
    go[x][0] = go[x][1] = 0;
}

void dfs2(int x, int fa, bool lim)
{
    for(int i = head[x]; i; i = e[i].nxt)
    {
        int id = e[i].to;
        if(id == fa || id == son[x])    continue;
        dfs2(id, x, 0);
    }
    if(son[x])
    {
        dfs2(son[x], x, 1);
        root[x] = root[son[x]];
    }
    else 
        root[x] = topt? tra[topt--]: ++ ndnum;
    for(int i = head[x]; i; i = e[i].nxt)
    {
        int id = e[i].to;
        if(id == fa || id == son[x])    continue;
        update(x, id, x);
        Union(x, id, x);
    }
    Add(root[x], xr[x]);
    ans[x] = max(ans[x], Query(root[x], x, xr[x]));
    if(!lim)
        rem(root[x]);
}

int main()
{
    freopen("irregular.in", "r", stdin);
    freopen("irregular.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for(int i = 1; i < n; ++i)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        Addedge(u, v);
        Addedge(v, u);
    }
    xr[1] = a[1];
    dfs1(1, 0);
    dfs2(1, 0, 1);
    for(int i = 1; i <= n; ++i)
        printf("%d ", ans[i]);
    return 0;
}
irregular

 

T3:unnormal

不正常序列

题目描述

  我们定义一个不正常序列

  $F_{i} = 1$

  $F_{i} = (a * M_{i}  + b * i + c) mod (10^{9} + 7) (i > 1) $

  在这里, $M_{i}$是指数列$F_{1}, F_{2},..., F_{i-1}$的中位数。

  一个数列的中位数是当这个数列拍好序号数列最中间的那个数, 如果数列一共有偶数项, 那么我们定义较小的那个为他的中位数。

  对于给定的a,b,c和n, 求和$F_{1} + F_{2} + ... + F_{n}$。

输入格式

  仅一行, a, b, c, n

输出格式

  一行表示数列的和

 

样例输入

5 6 7 2

样例输出

25

数据范围

  对于30%数据, 满足  n <= 50

  对于100% 数据, 满足n <= 106, 0 <= a, b, c <= 109 + 7

解析:

   其实就是要动态维护中位数

  唔...几天前看《算法竞赛》时看到了并搞过动态维护中位数, 结果就碰上了???

  当然是双堆, 书上叫对顶堆(P33),维护一个个数小于等于当前数的一半,值都比中位数大的小根堆, 再把剩下的数维护成一个大根堆, 中位数就是大根堆的堆顶了

  在打完堆的做法后我的机子上跑一个1e6的数据要跑1.01秒, 搞得我很慌, 又rush了一个值域线段树, 0.8秒可以跑完, 结果发现空间不够, 写了一个$Splay$, 结果1.5秒, 直接弃疗。最后还是用的堆, 但在hfu的机子上就过了, 早知道这样我就不再这道题上浪费这么多时间了, 还不如打一个$T2$的暴力,考试的策略还是存在问题,这种情况应该先想清楚时间和空间复杂度再动手, 而且应该先考虑容易拿的分, 应该多考虑效益, 这一点要在以后的考试中多注意。

 代码:

#include<bits/stdc++.h>
using namespace std;
const int mod = 1000000007;

priority_queue<int> p1, p2;

int main()
{
    freopen("unnormal.in", "r", stdin);
    freopen("unnormal.out", "w", stdout);
    register int a, b, c, n, siz1 = 1, siz2 = 0, res, tmp, i;
    register long long ans = 1;
    scanf("%d%d%d%d", &a, &b, &c, &n);
    p1.push(1);
    for(i = 2; i <= n; ++i)
    {
        res = (1LL * p1.top() * a + 1LL * b * i + 1LL * c) % mod;
        ans = ans + res;
        if(res < p1.top())
        {
            p1.push(res);
            ++ siz1;
        }
        else
        {
            p2.push(-res);
            ++ siz2;
        }
        if(siz1 > siz2 + 1)
        {
            -- siz1;
            ++ siz2;
            tmp = p1.top();
            p1.pop();
            p2.push(-tmp);
        }
        if(siz2 > siz1)
        {
            -- siz2;
            ++ siz1;
            tmp = p2.top();
            p2.pop();
            p1.push(-tmp);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
unnormal

猜你喜欢

转载自www.cnblogs.com/Joker-Yza/p/11593452.html