[CSP-S模拟测试]:模板(ac)(线段树启发式合并)
题目描述
辣鸡$ljh\ NOI$之后就退役了,然后就滚去学文化课了。
他每天都被$katarina$大神虐,仗着自己学过一些姿势就给$katarina$大神出了一道题。
有一棵$n$个节点的以$1$号节点为根的树,每个节点上有一个小桶,节点$u$上的小桶可以容纳个小球,$ljh$每次可以给一个节点到根路径上的所有节点的小桶内放一个小球,如果这个节点的小桶满了则不能放进这个节点,在放完所有小球之后就企图去***难$katarina$大神,让$katarina$大神回答每个节点的小桶内的小球有多少种颜色。
然而$katarina$大神一眼就秒掉了,还说这就是一道傻逼模板题。
现在$katarina$大神想考考即将参加$NOIP2019$的你能不能回答上辣鸡$ljh$的问题。
输入格式
第一行,一个整数$n$,树上节点的数量。
接下来$n-1$行,每行两个整数$u,v$,表示在$u,v$之间有一条边。
接下来一行$n$个整数,$k_1~k_n$表示每个节点上的小桶数量。
下一行是一个整数$m$,表示$ljh$进行的操作数量。
接下来$m$行,每行两个整数$x,c$,分别表示进行操作的节点和小球颜色。
下一行是一个整数$Q$,表示你需要回答的询问数。
接下来$Q$行,每行一个整数$x$,表示一个询问。
输出格式
对于每个询问输出一行表示这个询问的答案。
样例
样例输入1:
5
1 2
2 3
3 4
2 5
2 1 1 1 1
2
2 1
4 2
3
1
3
5
样例输出1:
2
1
0
样例输入2:
10
3 10
2 5
3 2
2 6
1 9
8 7
7 4
3 8
3 1
15 47 23 22 9 16 45 39 21 13
10
10 7
9 3
5 1
5 2
9 4
10 9
2 4
10 1
2 6
7 9
3
1
2
3
样例输出2:
7
4
6
数据范围与提示
对于$5\%$的数据,$n\leqslant 10,m\leqslant 10,k_i\leqslant 10$。
对于$30\%$的数据,$n\leqslant {10}^3,m\leqslant {10}^3,k_i\leqslant {10}^3$。
对于另外$40\%$的数据,$n\leqslant {10}^5,m\leqslant {10}^5,k_i={10}^5$。
对于$100\%$的数据,$n\leqslant {10}^5,m\leqslant {10}^5,k_i\leqslant {10}^5$。
题解
$5\%$算法:
到现在我都没想出来。
$30\%$算法:
暴力修改,$\Theta(1)$查找答案。
时间复杂度:$\Theta(n^2)$。
期望得分:$30$分。
$40\%$算法:
注意$k_i={10}^5$,也就是说,每一个桶肯定不会装超,问题也就简单了许多,转化成了[BZOJ3307]:雨天的尾巴这道题。
区别在于,这道题是维护总和,而那道题是维护最大值,而且这道题还不用LCA。
时间复杂度:$\Theta(n\log n)$。
期望得分:$40$分。
总得分:$70$分。
$100\%$算法:
对于每一个结点都用一棵线段树维护,下标为时间,存储这个时间子树中是否已经加入了小球和贡献(注意如果之前已经加入了这个小球则贡献为0)。
在线段树上二分查找答案就好了。
但是,显然这样还不够,无论是时间还是空间。
所以我们使用线段树启发式合并,利用启发式合并的方式处理出当前子树中的所有操作,同时构建出新的线段树。
我们还可以先预处理出重儿子,这样我们就可以更少的改变当前线段树里的值。
时间复杂度:$\Theta(n\log^2n)$。
期望得分:$100$分。
代码时刻
$30\%$算法:
#include<bits/stdc++.h> using namespace std; struct rec { int nxt; int to; }e[200001]; int N,M,Q; int head[100001],cnt; int k[100001]; int fa[100001]; bool Map[1001][1001]; int ans[100001]; void add(int x,int y) { e[++cnt].nxt=head[x]; e[cnt].to=y; head[x]=cnt; } void pre_dfs(int x)//预处理 { for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa[x]) { fa[e[i].to]=x; pre_dfs(e[i].to); } } void ufs(int x,int c)//疯狂往上找,暴力修改 { while(1) { if(!x)break; if(!Map[x][c]&&k[x]>0)ans[x]++; Map[x][c]=1; k[x]--; x=fa[x]; } } int main() { scanf("%d",&N); for(int i=1;i<N;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } for(int i=1;i<=N;i++) scanf("%d",&k[i]); scanf("%d",&M); if(N<=1000&&M<=1000) { pre_dfs(1); while(M--) { int x,c; scanf("%d%d",&x,&c); ufs(x,c); } scanf("%d",&Q); while(Q--) { int x; scanf("%d",&x); printf("%d\n",ans[x]); } } return 0; }
$40\%$算法:
#include<bits/stdc++.h> using namespace std; struct rec { int nxt; int to; }e[200001]; struct node { int x; int c; }q[100001]; int N,M,Q; int head[100001],cnt; int root[100001],trsum[10000001],ls[10000001],rs[10000001],tot; int k[100001]; int dfsv[100001]; bool vis[100001]; int sum; int ans[100001]; bool cmp(node a,node b){return a.c<b.c;} void add(int x,int y) { e[++cnt].nxt=head[x]; e[cnt].to=y; head[x]=cnt; } void insert(int &x,int k,int l,int r) { if(!x)x=++tot; if(l==r) { trsum[x]=1; return; } int mid=(l+r)>>1; if(k<=mid)insert(ls[x],k,l,mid); else insert(rs[x],k,mid+1,r); trsum[x]=trsum[ls[x]]+trsum[rs[x]]; } int merge(int x,int fl,int l,int r)//合并 { if(!x||!fl)return x+fl; if(l==r) { trsum[x]=trsum[x]||trsum[fl]; return x; } int mid=(l+r)>>1; ls[x]=merge(ls[x],ls[fl],l,mid); rs[x]=merge(rs[x],rs[fl],mid+1,r); trsum[x]=trsum[ls[x]]+trsum[rs[x]]; return x; } void dfs(int x) { vis[x]=1; for(int i=head[x];i;i=e[i].nxt) if(!vis[e[i].to]) { dfs(e[i].to); root[x]=merge(root[x],root[e[i].to],1,M); } ans[x]=trsum[root[x]]; } int main() { scanf("%d",&N); for(int i=1;i<N;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } for(int i=1;i<=N;i++) scanf("%d",&k[i]); scanf("%d",&M); for(int i=1;i<=M;i++) scanf("%d%d",&q[i].x,&q[i].c); sort(q+1,q+M+1,cmp); for(int i=1;i<=M;i++) { if(q[i].c!=q[i-1].c)sum++;//算是一个预处理吧,思路有些清奇 insert(root[q[i].x],sum,1,M); } dfs(1); scanf("%d",&Q); while(Q--) { int x; scanf("%d",&x); printf("%d\n",ans[x]); } return 0; }
$100\%$算法:
#include<bits/stdc++.h> #define L(x) x<<1 #define R(x) x<<1|1 using namespace std; struct rec { int nxt; int to; }e[200001]; struct node { int x; int c; }q[100001]; int N,M,Q; int head[100001],cnt; int t[100001]; map<int,int> col;//会有负的权值,所以用map int sum; bool vis[100001]; int son[100001]; int tr[400001],lz[400001],tle[400001],size[400001]; int ans[100001]; vector<pair<int,int> >super[100001];//用vector存储每一个点内球的颜色和时间,二维数组会MLE void add(int x,int y) { e[++cnt].nxt=head[x]; e[cnt].to=y; head[x]=cnt; } void super_memset(int x)//清空 { tr[1]=size[1]=0; lz[1]=1; for(int i=0;i<super[x].size();i++)tle[super[x][i].first]=0; } void pushup(int x) { tr[x]=tr[L(x)]+tr[R(x)]; size[x]=size[L(x)]+size[R(x)]; } void pushdown(int x) { if(!lz[x])return; size[L(x)]=size[R(x)]=tr[L(x)]=tr[R(x)]=lz[x]=0; lz[L(x)]=lz[R(x)]=1; } void change(int x,int l,int r,int k,int v,int w) { tr[x]+=v; size[x]+=w; if(l==r)return; pushdown(x); int mid=(l+r)>>1; if(k<=mid)change(L(x),l,mid,k,v,w); else change(R(x),mid+1,r,k,v,w); pushup(x); } void add(int x) { for(int i=0;i<super[x].size();i++) { pair<int,int> flag=super[x][i]; if(!tle[flag.first]) { change(1,1,M,flag.second,1,0); tle[flag.first]=flag.second; } else if(tle[flag.first]>flag.second) { change(1,1,M,tle[flag.first],-1,0); change(1,1,M,flag.second,1,0); tle[flag.first]=flag.second; } change(1,1,M,flag.second,0,1); } } int ask(int x,int l,int r,int k) { if(k<=0)return 0; if(l==r)return tr[x]; pushdown(x); int mid=(l+r)>>1; if(size[L(x)]<=k)return tr[L(x)]+ask(R(x),mid+1,r,k-size[L(x)]); else return ask(L(x),l,mid,k); } void connect(int x,int y) { for(int i=0;i<super[y].size();i++) super[x].push_back(super[y][i]); super[y].clear(); } void pre_dfs(int x) { size[x]=super[x].size(); vis[x]=1; for(int i=head[x];i;i=e[i].nxt) if(!vis[e[i].to]) { pre_dfs(e[i].to); size[x]+=size[e[i].to]; if(size[e[i].to]>size[son[x]])son[x]=e[i].to; } } void pro_dfs(int x,int fa) { for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa&&e[i].to!=son[x]) { pro_dfs(e[i].to,x); super_memset(e[i].to); } if(son[x])pro_dfs(son[x],x); add(x); for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa&&e[i].to!=son[x]) add(e[i].to); ans[x]=ask(1,1,M,t[x]); if(son[x]) { connect(son[x],x); swap(super[x],super[son[x]]); for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa) connect(x,e[i].to); } } int main() { scanf("%d",&N); for(int i=1;i<N;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } for(int i=1;i<=N;i++) scanf("%d",&t[i]); scanf("%d",&M); for(int i=1;i<=M;i++) { scanf("%d%d",&q[i].x,&q[i].c); if(!col[q[i].c]) { col[q[i].c]=++sum; q[i].c=sum; } else q[i].c=col[q[i].c]; super[q[i].x].push_back(make_pair(q[i].c,i)); } pre_dfs(1); memset(size,0,sizeof(size)); pro_dfs(1,0); scanf("%d",&Q); while(Q--) { int x; scanf("%d",&x); printf("%d\n",ans[x]); } return 0; }
rp++