【Codeforces967E】May Holidays

本文探讨了一种解决员工休假管理问题的算法。在一个树状结构的公司中,需要跟踪每位员工及其下属的休假状态,并实时计算不满员工的数量。通过构建虚拟树结构,对查询进行分块处理,有效地解决了该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

time limit: per test 5 seconds
memory limit: per test 256 megabytes
standard input
standard output
Description
It’s May in Flatland, and there are m days in this month. Despite the fact that May Holidays are canceled long time ago, employees of some software company still have a habit of taking short or long vacations in May.

Of course, not all managers of the company like this. There are n employees in the company that form a tree-like structure of subordination: each employee has a unique integer id i between 1 and n, and each employee with id i (except the head manager whose id is 1) has exactly one direct manager with id pi. The structure of subordination is not cyclic, i.e. if we start moving from any employee to his direct manager, then we will eventually reach the head manager. We define that an employee u is a subordinate of an employee v, if v is a direct manager of u, or the direct manager of u is a subordinate of v. Let si be the number of subordinates the i-th employee has (for example, s1=n−1, because all employees except himself are subordinates of the head manager).

Each employee i has a bearing limit of ti, which is an integer between 0 and si. It denotes the maximum number of the subordinates of the i-th employee being on vacation at the same moment that he can bear. If at some moment strictly more than ti subordinates of the i-th employee are on vacation, and the i-th employee himself is not on a vacation, he becomes displeased.

In each of the m days of May exactly one event of the following two types happens: either one employee leaves on a vacation at the beginning of the day, or one employee returns from a vacation in the beginning of the day. You know the sequence of events in the following m days. Your task is to compute for each of the m days the number of displeased employees on that day.

Input
The first line contains two integers n and m (2n,m105)(2≤n,m≤105) — the number of employees in the company and the number of days in May.

The second line contains n−1 integers p2,p3,…,pn (1≤pi≤n), denoting the direct managers of employees.

The third line contains n integers t1,t2,…,tn (0≤ti≤si), denoting the bearing limits of empoyees.

The fourth line contains m integers q1,q2,…,qm (1≤|qi|≤n, qi≠0), denoting the events. If qi is positive, then the employee with id qi leaves for a vacation starting from this day, if qi is negative, then the employee −qi returns from a vacation starting from this day. In the beginning of May no employee is on vacation. It is guaranteed that if some employee leaves for a vacation, he is not on a vacation at the moment and vice versa.

Output
Print a sequence of m integers a1,a2,…,am, where ai is the number of displeased employees on the i-th day.

Examples
input1

7 8
4 5 1 1 5 5
0 0 0 1 2 0 0
2 6 3 7 -2 4 -3 1

output1

1 1 1 2 2 2 1 0

input2

5 6
1 2 3 4
4 0 0 1 0
1 5 2 3 -5 -1

output2

0 2 1 0 0 0

Hint
In the first sample test after employee with id 2 leaves for a vacation at the first day, the head manager with id 1 becomes displeased as he does not want any of his subordinates to go for a vacation. At the fourth day employee with id 5 becomes displeased as his last remaining employee with id 7 leaves for a vacation. At the fifth day employee with id 2 returns from the vacation, but it does not affect the number of displeased employees as the employees 5 and 1 are still displeased. At the sixth day employee with id 3 returns back from the vacation, preventing the employee with id 5 from being displeased and at the last day the head manager with id 1 leaves for a vacation, leaving the company without the displeased people at all.


题目大意
一个以1为根的树形结构,n个点,每个点有个参数ti
有m次操作,每次给第qi的点打上标记或取消标记之后并询问这棵树上有多少个点i满足它没标记,且出自己以外的子树中多于ti个点被打上标记


Analysis

一看这题就很皮
这题显然可以转换成:打标记则将他从父亲到1的节点t值减一,询问没标记且t值为负的节点个数
考虑将询问分块,求每个块对答案的影响
对于每一个块,只有块内的元素标记会改变,那么拿块内的点建一颗虚树,将虚树中相邻的点对应原来的链拉出来(不包括端点),按t值从小到大排个序,压缩重复的值,那指针标记t值小于0的最右边的位置
接着枚举每一个操作,将点x到1的每一条链都进行求值:端点判断一下,中间用指针扫一下。并将这条链打上t加或减的tag
最后将tag还原回原树
复杂度O(mk(nlogn+k2))O(mk(nlog⁡n+k2))k为块的大小,也是虚树的大小
k=nlognk=nlog⁡n时复杂度最小,为O(mnlogn)O(mnlog⁡n)
当然如果用基数排序可以做到O(mn)O(mn)

code

#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100100
#define G 1300

using namespace std;

int q[N],t[N],n,m,to[N+N],fir[N],nex[N+N],top,f[20][N],Fa[N],ans;
int dfn[N],sta[N],k[N],T,cnt,dep[N],dot[N],tot,tag[N],pt[N],left[N],right[N],out[N];
struct node{int bl,w,t,v;}d[N],tmp[N];
bool cmp(node a,node b){return a.bl<b.bl || (a.bl==b.bl && a.t<b.t);}
bool cmp2(int a,int b){return dfn[a]<dfn[b];}

void link(int x,int y){
    to[++top]=y;nex[top]=fir[x];fir[x]=top;
}
void dfs(int w){
    dfn[w]=++top;dep[w]=dep[f[0][w]]+1;
    for(int i=fir[w];i;i=nex[i])if(to[i]!=f[0][w])f[0][to[i]]=w,dfs(to[i]);
}
int lca(int x,int y){
    if(dep[x]>dep[y])return lca(y,x);
    for(int i=19;i+1;i--)if(dep[f[i][y]]>=dep[x])y=f[i][y];
    if(x==y)return x;
    for(int i=19;i+1;i--)if(f[i][y]!=f[i][x])x=f[i][x],y=f[i][y];
    return f[0][x];
}

void con(int x,int y){Fa[x]=y;}
void deal(int bl,int x,int edge){
    if(x==edge)return;
    d[++cnt]=(node){bl,x,t[x],1};
    deal(bl,f[0][x],edge);
}
void add(int bl,int x,int edge){
    if(x==edge)return;
    t[x]+=tag[bl];add(bl,f[0][x],edge);
}

int main(){
    scanf("%d %d",&n,&m);
    for(int i=2;i<=n;i++){
        int x;scanf("%d",&x);link(x,i);link(i,x);
    }top=0;dfs(1);
    for(int j=1;j<20;j++)for(int i=1;i<=n;i++)f[j][i]=f[j-1][f[j-1][i]];
    for(int i=1;i<=n;i++)scanf("%d",&t[i]);
    for(int i=1;i<=m;i++)scanf("%d",&q[i]);
    for(int i=1,l=0;i<=m;i++)if(i%G==0 || i==m){
        cnt=0;
        for(int j=i;j>l;j--)k[++cnt]=abs(q[j]);
        sort(k+1,k+cnt+1,cmp2);tot=0;
        for(int j=1,x=cnt;j<=x;j++)if(j==1)cnt=1;else if(k[j]!=k[j-1])k[++cnt]=k[j];
        for(int j=1;j<=cnt;j++)if(!T)sta[++T]=k[j];else{
            int x=lca(sta[T],k[j]);
            if(x!=sta[T]){
                while(T>1 && dep[sta[T-1]]>dep[x])dot[++tot]=sta[T--],con(sta[T+1],sta[T]);
                con(dot[++tot]=sta[T--],x);if(sta[T]!=x)sta[++T]=x;
            }sta[++T]=k[j];
        }for(;T>1;dot[++tot]=sta[T--])con(sta[T],sta[T-1]);
        dot[++tot]=sta[1];if(sta[1]!=1)con(sta[1],1),dot[++tot]=1;T=0;
        cnt=0;T=0;
        for(int j=1;j<=tot;j++){
            int x=dot[j],las=cnt;tag[x]=0;left[x]=0;right[x]=-1;pt[x]=0;
            if(x>1)deal(x,f[0][x],Fa[x]);sort(d+las+1,d+cnt+1,cmp);
            for(int k=las+1,p=0;k<=cnt;k++)if(!out[d[k].w]){
                if(!p)tmp[++T]=d[k],p=1;else if(d[k].t==tmp[T].t)tmp[T].v++;else tmp[++T]=d[k];
                if(!left[x])left[x]=T;right[x]=T;if(tmp[T].t<0)pt[x]=T;
            }
        }
        for(int j=1;j<=cnt;j++)if(!pt[d[j].bl])pt[d[j].bl]=left[d[j].bl]-1;
        for(int j=l+1;j<=i;j++){
            if(q[j]<0){
                int x=-q[j];
                out[x]=0;if(t[x]<0)ans++;
                for(;x>1;){
                    tag[x]++;while(pt[x]>=left[x] && tmp[pt[x]].t+tag[x]>=0)ans-=tmp[pt[x]--].v;
                    x=Fa[x];t[x]++;if(t[x]==0 && !out[x])ans--;
                }
            }else{
                int x=q[j];
                out[x]=1;if(t[x]<0)ans--;
                for(;x>1;){
                    tag[x]--;while(pt[x]<right[x] && tmp[pt[x]+1].t+tag[x]<0)ans+=tmp[++pt[x]].v;
                    x=Fa[x];t[x]--;if(t[x]==-1 && !out[x])ans++;
                }
            }printf("%d\n",ans);
        }
        for(int j=1;j<=tot;j++){
            int x=dot[j];if(x>1)add(x,f[0][x],Fa[x]);
        }
        l=i;T=0;
    }
    return 0;
}
### Codeforces 887E Problem Solution and Discussion The problem **887E - The Great Game** on Codeforces involves a strategic game between two players who take turns to perform operations under specific rules. To tackle this challenge effectively, understanding both dynamic programming (DP) techniques and bitwise manipulation is crucial. #### Dynamic Programming Approach One effective method to approach this problem utilizes DP with memoization. By defining `dp[i][j]` as the optimal result when starting from state `(i,j)` where `i` represents current position and `j` indicates some status flag related to previous moves: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = ...; // Define based on constraints int dp[MAXN][2]; // Function to calculate minimum steps using top-down DP int minSteps(int pos, bool prevMoveType) { if (pos >= N) return 0; if (dp[pos][prevMoveType] != -1) return dp[pos][prevMoveType]; int res = INT_MAX; // Try all possible next positions and update 'res' for (...) { /* Logic here */ } dp[pos][prevMoveType] = res; return res; } ``` This code snippet outlines how one might structure a solution involving recursive calls combined with caching results through an array named `dp`. #### Bitwise Operations Insight Another critical aspect lies within efficiently handling large integers via bitwise operators instead of arithmetic ones whenever applicable. This optimization can significantly reduce computation time especially given tight limits often found in competitive coding challenges like those hosted by platforms such as Codeforces[^1]. For detailed discussions about similar problems or more insights into solving strategies specifically tailored towards contest preparation, visiting forums dedicated to algorithmic contests would be beneficial. Websites associated directly with Codeforces offer rich resources including editorials written after each round which provide comprehensive explanations alongside alternative approaches taken by successful contestants during live events. --related questions-- 1. What are common pitfalls encountered while implementing dynamic programming solutions? 2. How does bit manipulation improve performance in algorithms dealing with integer values? 3. Can you recommend any online communities focused on discussing competitive programming tactics? 4. Are there particular patterns that frequently appear across different levels of difficulty within Codeforces contests?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值