~Rikka with Employees~ stcm
Rikka with Employees
这题好像有个哥哥叫 [Ynoi2008] stcm 来着,看起来应该比这题的限制更严。
考虑将树拍成 dfn 序,则 \(u\) 对应的子树为 \([\text{dfn}(u),\text{dfn}(u)+\text{size}(u)-1]\)。进行分治,假设分治到 \([l,r]\) 时,标记了 \([1,l-1] \cup [r+1,n]\) 中的所有点,此时我们要处理跨过 \(mid\) 的所有询问。
由于树存在偏序关系,所以跨过 \(mid\) 的询问也存在偏序关系,依次处理即可。
下面是 stcm 的代码:
//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int fa[100010],dfn[100010],rev[100010],siz[100010],tot;
vector<int> G[100010];
void dfs(int u){
tot++;
dfn[u]=tot;
rev[tot]=u;
siz[u]=1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
dfs(v);
siz[u]+=siz[v];
}
}
struct Node{
int l,r,id;
};
bool operator <(const Node &lhs,const Node &rhs){
return lhs.l<rhs.l;
}
void solve(int l,int r,vector<Node> vec){
if(vec.empty()){
return ;
}
if(l==r){
printf("=%d",vec[0].id);
return ;
}
int l_mid=(l+r)>>1;
int r_mid=l_mid+1;
vector<Node> l_vec,r_vec,m_vec;
for(int i=0;i<vec.size();i++){
if(vec[i].r<=l_mid){
l_vec.push_back(vec[i]);
}
else if(vec[i].l>=r_mid){
r_vec.push_back(vec[i]);
}
else{
m_vec.push_back(vec[i]);
}
}
sort(m_vec.begin(),m_vec.end());
int l_pre=l-1,r_pre=r+1;
for(int i=0;i<m_vec.size();i++){
int L=m_vec[i].l,R=m_vec[i].r,id=m_vec[i].id;
while(l_pre<L-1){
l_pre++;
printf("+%d",rev[l_pre]);
}
while(r_pre>R+1){
r_pre--;
printf("+%d",rev[r_pre]);
}
printf("=%d",id);
}
while(l_pre>=l){
printf("-");
l_pre--;
}
while(r_pre<=r){
printf("-");
r_pre++;
}
while(l_pre<l_mid){
l_pre++;
printf("+%d",rev[l_pre]);
}
solve(r_mid,r,r_vec);
while(l_pre>=l){
printf("-");
l_pre--;
}
while(r_pre>r_mid){
r_pre--;
printf("+%d",rev[r_pre]);
}
solve(l,l_mid,l_vec);
while(r_pre<=r){
printf("-");
r_pre++;
}
}
int main(){
int n;
while(~scanf("%d",&n)){
tot=0;
for(int i=1;i<=n;i++){
G[i].clear();
}
for(int i=2;i<=n;i++){
scanf("%d",&fa[i]);
G[fa[i]].push_back(i);
}
dfs(1);
vector<Node> vec;
for(int i=1;i<=n;i++){
vec.push_back((Node){dfn[i],dfn[i]+siz[i]-1,i});
}
solve(1,n,vec);
printf("!\n");
}
return 0;
}

浙公网安备 33010602011771号