Codeforces Round #457 (Div. 2)D,E详解

D. Jamie and To-do List
题意:Jamie有许多事情要做,于是他把事情弄了一个优先级(数字越小优先级越高),现在有三个操作,1,set a x,(a是char*型,x是int型,下同),代表把a事情的优先级变成x。2,remove a,把a事情移除掉。3,query a,询问比a事情优先级高的事情有多少个。4,undo d,撤销前d个操作(不包括当前这个)。
思路:如果没有操作4的话,我们可以map来实现字符串与数字的映射,然后可以用树状数组或者线段树来计数。但现在有操作4,用map肯定超空间,所以我们应该用一个可持久化的数据结构来存字符串,也就是字典树,而计数也可以用01字典树。
博主看了一下题解,题解写的是指针类型的字典树,那为什么不用数组呢?1是每个状态的字典树的大小不同,2是在转换的时候,我们还需要维护每个字典树的头结点的下标是哪一个,再通过这个来转换,而指针类型的就一个等于号就行了。所以相比而言这题用指针来写(主要是第一个原因,如果用数组的话不知道开多大的数组。。)
代码如下:

#include<iostream>
#include<string>
using namespace std;

struct Trie
{
    int dat;
    Trie *to[26];
    Trie()
    {
        dat = -1;
        for (int i = 0;i < 26;i++)
            to[i] = nullptr;
    }
    Trie(Trie *old)
    {
        dat = old->dat;
        for (int i = 0;i < 26;i++)
            to[i] = old->to[i];
    }

    Trie *set(string &s, int val, int pos = 0)
    {
        Trie *rt = new Trie(this);
        if (pos >= s.size()) {
            rt->dat = val;
        }
        else {
            int v = s[pos] - 'a';
            if (!to[v]) to[v] = new Trie();
            rt->to[v] = to[v]->set(s, val, pos + 1);
        }
        return rt;
    }

    int get(string &s, int pos = 0) {
        if (pos >= s.size()) {
            return dat;
        }
        else {
            int v = s[pos] - 'a';
            if (!to[v]) return -1;
            return to[v]->get(s, pos + 1);
        }
    }

};

struct BinaryTrie
{
    int cnt;
    BinaryTrie *to[2];
    BinaryTrie()
    {
        cnt = 0;
        to[0] = nullptr;
        to[1] = nullptr;
    }
    BinaryTrie(BinaryTrie *old)
    {
        cnt = old->cnt;
        to[0] = old->to[0];
        to[1] = old->to[1];
    }

    BinaryTrie *set(int num, int dat, int dep = 30)
    {
        BinaryTrie *temp = new BinaryTrie(this);
        temp->cnt += dat;
        if (dep >= 0)
        {
            int v = (num >> dep) & 1;
            if (!to[v])to[v] = new BinaryTrie();
            temp->to[v] = to[v]->set(num, dat, dep - 1);
        }
        return temp;
    }

    int get(int num,int dep=30)
    {
        if (dep < 0)return 0;
        int v = (num >> dep) & 1;
        if (v)
        {
            int ans = 0;
            if (to[0])
                ans += to[0]->cnt;
            if (to[1])
                ans += to[1]->get(num, dep - 1);
            return ans;
        }
        else
        {
            if (to[0])
                return to[0]->get(num,dep-1);
            return 0;
        }
    }

};


int main()
{
    ios::sync_with_stdio(0);
    int q;
    cin >> q;
    string ord, a;
    int b;
    Trie **Tri = new Trie *[q + 5];
    BinaryTrie **Bin = new BinaryTrie *[q + 5];

    Tri[0] = new Trie();
    Bin[0] = new BinaryTrie();
    for (int t = 1;t <= q;t++)
    {
        cin >> ord;
        if (ord[0] == 's')
        {
            cin >> a >> b;
            int oldnum = Tri[t - 1]->get(a);
            Tri[t] = Tri[t - 1]->set(a, b);
            if (oldnum != -1)
            {
                Bin[t] = Bin[t - 1]->set(oldnum, -1);
                Bin[t] = Bin[t]->set(b, 1);
            }
            else
                Bin[t] = Bin[t-1]->set(b, 1);
        }
        else if (ord[0] == 'q')
        {
            cin >> a;
            Tri[t] =Tri[t - 1];
            Bin[t] =Bin[t - 1];
            int num = Tri[t]->get(a);
            if(num!=-1)
                cout << Bin[t]->get(num) << endl;
            else cout << -1 << endl;
        }
        else if (ord[0] == 'r')
        {
            cin >> a;
            int oldnum = Tri[t - 1]->get(a);
            if (oldnum != -1)
            {
                Tri[t] = Tri[t - 1]->set(a, -1);
                Bin[t] = Bin[t - 1]->set(oldnum, -1);
            }
            else
            {
                Tri[t] = Tri[t - 1];
                Bin[t] = Bin[t - 1];
            }
        }
        else if (ord[0] == 'u')
        {
            cin >> b;
            Tri[t] = Tri[t-1-b];
            Bin[t] = Bin[t - 1 - b];
        }
    }
    return 0;
}

这份代码的结构体部分和官方题解差不多,博主本来想把Trie的set函数修改成这样

Trie *set(string s,int num)
    {
        int Size = s.size();
        Trie *temp = new Trie(this);
        Trie *root = temp;
        for (int i = 0;i < Size;i++)
        {
            int v = s[i] - 'a';
            if (!temp->to[v])
                temp->to[v] = new Trie();
            temp = temp->to[v];
        }
        temp->dat = num;
        return root;
    }

但不知道为啥wa了。。。希望有大佬告诉我原因。。
E. Jamie and Tree
思路:先简单来看,如果不换根的话,这道题就变简单了许多,首先把它转换成dfs序,维护每个结点的位置和子树Size,把它存进线段树维护区间和就行了。
那么换根后呢?
下面部分我们要仔细思考!
首先设当前根为root。
第一个问题如何找到修改根后的lca(u,v)。
可以发现,如果u和v都是root的子节点,那么fa=lca(u,v)。
如果u和v其中一个是r的子节点那么fa=r。
如果都不是呢,设p=lca(u,r),q=lca(v,r),t=lca(u,r).那么fa等于p,q,r的深度最大的那个。(思考思考)。
第二个问题如何更新。
如果fa=root,那么直接更新整棵树。
如果fa是root的祖先,那么就更新fa的子树但不包括root的那颗(思考)。
其他情况就直接更新fa的子树了。
第三个问题如何查询。
查询其实和更新差不多。
如果v=root,那么就询问整棵树。
如果v=root的祖先,那么询问整棵树的值减掉不包括root的那颗树的值。
其他情况直接询问v的子树就行了。

#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
ll val[maxn];
vector<int>V[maxn];

int node[maxn], tot;
int st[maxn][20];
int dep[maxn];
int n, q;
int nodeidx[maxn];
int Size[maxn];
void dfs(int u, int pre,int d)
{
    node[++tot] = u;
    nodeidx[u] = tot;
    dep[u] = d;
    for (auto it : V[u])
        if(it!=pre)
    {
        st[it][0] = u;
        dfs(it, u,d+1);
        Size[u] += Size[it];
    }
    Size[u]++;
}

void init()
{
    for(int i=1;i<20;i++)
        for (int j = 1;j <= n;j++)
        {
            if ((1 << i) > dep[j])continue;
            st[j][i] = st[st[j][i - 1]][i - 1];
        }
}

int query(int x, int y)
{
    if (dep[x] > dep[y])swap(x, y);
    for (int j = 20;j >= 0 && dep[x] != dep[y];j--)
    {
        if(dep[y] - (1 << j) < dep[x])continue;
        y = st[y][j];
    }
    if (x == y)return x;
    for (int j = 20;j >= 0;j--)
    {
        if (dep[x] - (1 << j) < 0 || st[x][j] == st[y][j])continue;
        x = st[x][j], y = st[y][j];
    }
    return st[x][0];
}


#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
ll Sum[maxn << 2], Add[maxn << 2];
ll A[maxn];

void init2()
{
    for (int i = 1;i <= n;i++)
    {
        A[i] = val[node[i]];
    }
}

void PushUp(int rt)
{
    Sum[rt] = Sum[rt << 1] + Sum[rt << 1 | 1];
}

void Build(int l, int r, int rt)
{
    if (l == r)
    {
        Sum[rt] = A[l];
        return;
    }
    int m = (l + r) >> 1;
    Build(ls);
    Build(rs);
    PushUp(rt);
}

void PushDown(int rt, int ln, int rn)
{
    if (Add[rt])
    {
        Add[rt << 1] += Add[rt];
        Add[rt << 1 | 1] += Add[rt];
        Sum[rt << 1] += Add[rt] * ln;
        Sum[rt << 1 | 1] += Add[rt] * rn;
        Add[rt] = 0;
    }
}

void Update(int L, int R, int C, int l, int r, int rt)
{
    if (L <= l&&r <= R)
    {
        Sum[rt] += 1LL*C*(r - l + 1);
        Add[rt] +=C;
        return;
    }
    int m = (l + r) >> 1;
    PushDown(rt, m - l + 1, r - m);
    if (L <= m)
        Update(L, R, C, ls);
    if (R > m)
        Update(L, R, C, rs);
    PushUp(rt);
}


ll Query(int L, int R, int l, int r, int rt)
{
    if (L <= l&&r <= R)
        return Sum[rt];
    int m = (l + r) >> 1;
    PushDown(rt, m - l + 1, r - m);
    ll ANS = 0;
    if (L <= m)ANS += Query(L, R, l, m, rt << 1);
    if (R > m)ANS += Query(L, R, m + 1, r, rt << 1 | 1);
    return ANS;
}

int find(int de,int root)
{
    int rtmp = root;
    for (int i = 20;i >= 0;i--)if ((de >> i)&1)
        rtmp = st[rtmp][i];
    return rtmp;
}

int main()
{

    scanf("%d %d", &n, &q);
    for (int i = 1;i <= n;i++)
        scanf("%lld", &val[i]);
    int u, v;
    for (int i = 1;i < n;i++)
    {
        scanf("%d %d", &u, &v);
        V[u].push_back(v);
        V[v].push_back(u);
    }
    dfs(1, 0,0);
    init();
    init2();
    Build(1, n, 1);
    int ques, x;
    int root = 1;
    for (int i = 1;i <= q;i++)
    {
        scanf("%d", &ques);
        if (ques == 1)
        {
            scanf("%d", &v);
            root = v;
        }
        else if (ques == 2)
        {
            scanf("%d %d %d", &u, &v, &x);
            int uv = query(u, v);
            int ur = query(root, u);
            int vr = query(root, v);
            int fa;
            if (ur == root&&vr == root)
                fa = uv;
            else if (ur == root || vr == root)
                fa = root;
            else
            {
                fa = dep[ur] > dep[vr] ? ur : vr;
                fa = dep[fa] > dep[uv] ? fa : uv;
            }
            if (fa == root)
            {
                Update(1, n, x, 1, n, 1);
            }
            else
            {
                int _far = query(fa, root);
                if (_far == fa)
                {
                    int d = dep[root] - dep[fa];
                    int no = find(d - 1, root);
                    Update(1, n, x, 1, n, 1);
                    Update(nodeidx[no], nodeidx[no] + Size[no] - 1, -x, 1, n, 1);
                }
                else
                {
                    Update(nodeidx[fa], nodeidx[fa] + Size[fa] - 1, x, 1, n, 1);
                }
            }
        }
        else
        {
            scanf("%d", &v);
            int vr = query(v, root);
            if (v == root)
            {
                printf("%lld\n", Query(1, n, 1, n, 1));
            }
            else if (vr == v)
            {
                int d = dep[root] - dep[vr];
                int no = find(d - 1, root);
                printf("%lld\n", Query(1, n, 1, n, 1) - Query(nodeidx[no], nodeidx[no] + Size[no] - 1, 1, n, 1));
            }
            else
            {
                printf("%lld\n", Query(nodeidx[v], nodeidx[v] + Size[v]-1, 1, n, 1));
            }
        }

    }
    return 0;
}

可以从这两个问题发现,一个复杂的问题往往是一个简单的问题转换过来的,所以遇到该类问题尽量转换成简单的问题,再思考难的那个部分该怎么解决。

好久没写博客了,也不知道写的好不好。。如果博客有不足的地方,希望各位读者告诉博主。大家一起加油。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值