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;
}
可以从这两个问题发现,一个复杂的问题往往是一个简单的问题转换过来的,所以遇到该类问题尽量转换成简单的问题,再思考难的那个部分该怎么解决。
好久没写博客了,也不知道写的好不好。。如果博客有不足的地方,希望各位读者告诉博主。大家一起加油。。