传送门:Q974
题目描述
lester是"VVJFJ方熊玥题"公司的老板,共有n名员工,工号分别为0...n-1(lester本人是0号员工)。公司是一个官僚体系,除lester外每个人有且仅有一个直属上级。为了营(zhi)造(zao)氛(hun)围(luan),lester决定不时让一些员工休假。刚开始时,所有员工都是工作状态,lester会依次下发m条指令,每条指令由两个正整数t,x表示。具体含义如下
t=1,员工x及其所有上级(包括间接上级)停止工作,开始休假。如其中有人已经在休假状态,则保持休假
t=2,员工x及其所有下级(包括间接下级)结束休假,开始工作。如其中有人已经在工作状态,则保持工作
(聪明的你会发现根据这种规则,lester自己大部分时间都在休假^^)
现在lester想知道每次指令下发以后,会导致多少人的状态发生改变(即由工作变为休假,或由休假变为工作)
输入格式
输入文件company2.in 第1行一个正整数n(<=100000) 第2行n-1个整数,分别表示员工1..n-1的直属上级编号(数据保证上级关系不会形成环) 第3行1个正整数m(<=100000) 后面m行每行2个整数t,x(t=1,2,0<=x<=n-1)
输出格式
输出文件company2.out 输出m行每行1个整数,表示答案
输入输出样例
输入样例#1:
4
0 1 1
3
1 2
1 3
2 1
输出样例#1:
3
1
3
思路
用线段树(重链剖分)+分块可以解决这道题,但注意原题给的是0~n-1,要改成1~n
还有一些注意点我将会在后续提到
代码
1.主函数
cin>>n;
for(ll i=2;i<=n;i++){
cin>>p[i];
p[i]++;//把编号从0~n-1,改为1~n
adde(p[i],i);
}
dfs_son(1);//初始化
dfs_top_t(1);
for(ll i=1;i<=n;i++){
a[i]=1;
}
B=sqrt(n)+1;
// B=1;
for(ll i=1;i<=n;i++){
b[i]=(i-1)/B+1;
// cout<<p[i]<<endl;
}
// cout<<"******";
for(ll i=1;i<=n;i++){
bs[b[i]]+=a[i];
}
for(ll i=1;i<=b[n];i++){
tag[i]=2;
}
cin>>m;
for(ll i=1;i<=m;i++){
ll t,x;
cin>>t>>x;
x++;//把编号从0~n-1,改为1~n
if(t==1){
hld(x);
}else{
wrk(x);
}
}
主要是重链剖分+分块初始化。
2.重链剖分函数部分
void dfs_son(ll u){
d[u]=d[p[u]]+1;
sz[u]=1;
son[u]=0;
for(ll i=hd[u];i;i=nxt[i]){
ll v=to[i];
dfs_son(v);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]){
son[u]=v;
}
}
}
void dfs_top_t(ll u){
tI[u]=++timer;
tO[u]=tI[u]+sz[u]-1;
if(son[p[u]]==u){
top[u]=top[p[u]];
}else{
top[u]=u;
}
if(!son[u]){
return;
}
dfs_top_t(son[u]);
for(ll i=hd[u];i;i=nxt[i]){
ll v=to[i];
if(v==son[u]){
continue;
}
dfs_top_t(v);
}
}
不用多说,模板。
3.分块
1)信息下放
把tag[iB]的标记暴力赋值给块中a[i](tag为2则不用操作,因为没有操作)
其中a[i]表示i的状态:
0表示在休假;
1表示在工作。
void pushdown(ll iB){//信息下放
if(tag[iB]==2){
return;
}
ll l=B*(iB-1)+1,r=min(n,B*iB);//防止越界
for(ll u=l;u<=r;u++){
a[u]=tag[b[u]];//***不是tag[u],应为tag[iB] or tag[b[u]]
}
tag[iB]=2;
}
!!!tag[iB]是每块的状态!
2)改变状态
分为wrk(x),hld(x)两种(下级工作或上级休假)
void hld(ll x){//上级休假
ll ans=0;
while(x){
ans+=rsq(tI[top[x]],tI[x]);
upd(tI[top[x]],tI[x],0);
x=p[top[x]];
}
cout<<ans<<endl;
}
void wrk(ll x){//下级工作
ll ans=sz[x]-rsq(tI[x],tO[x]);
upd(tI[x],tO[x],1);
cout<<ans<<endl;
}
3)操作后的查询,更新
即upd()和rsq()
void upd(ll l,ll r,ll w){//更新
pushdown(b[l]);
pushdown(b[r]);
if(b[l]==b[r]){
for(ll i=l;i<=r;i++){
bs[b[i]]+=w-a[i];
a[i]=w;
}
return ;
}
for(ll i=b[l]+1;i<b[r];i++){
tag[i]=w;
bs[i]=w*B;
}
for(ll i=l;b[i]==b[l];i++){
bs[b[i]]+=w-a[i];
a[i]=w;
}
for(ll i=r;b[i]==b[r];i--){
bs[b[i]]+=w-a[i];
a[i]=w;
}
}
ll rsq(ll l,ll r){//查询工作人数
pushdown(b[l]);
pushdown(b[r]);
ll ans=0;
if(b[l]==b[r]){
for(ll i=l;i<=r;i++){
ans+=a[i];
}
return ans;
}
for(ll i=b[l]+1;i<b[r];i++){
ans+=bs[i];
}
for(ll i=l;b[i]==b[l];i++){
ans+=a[i];
}
for(ll i=r;b[i]==b[r];i--){
ans+=a[i];
}
return ans;
}
以上就是这题的解析。
1万+

被折叠的 条评论
为什么被折叠?



