正题

题目连接:https://www.luogu.com.cn/problem/P8339


题目大意

给出nnn个点的一棵树,每个点有钥匙或者宝箱,有不同的颜色。

mmm次询问,从xxx走到yyy,走到钥匙时会拾取钥匙,走到宝箱时如果有同色的钥匙那么就会消耗一把钥匙打开宝箱,询问能打开多少个宝箱。

保证每一种颜色的钥匙不超过555把。

1≤n≤5×105,1≤m≤1061\leq n\leq 5\times 10^5,1\leq m\leq 10^61≤n≤5×105,1≤m≤106


解题思路

先考虑同色的宝箱和钥匙都只有一个的情况,这是一个经典问题,假设分别为x,yx,yx,y,那么删去x↔yx\leftrightarrow yx↔y的路径,xxx的联通块记为SSS,yyy的联通块记为TTT。

如果询问节点起点在SSS,终点在TTT就会产生贡献。

那么SSS和TTT要么两个都是子树,要么一个是子树,另一个是整棵树删去一个子树,也就是说它们都可以表示成dfsdfsdfs序上的一个或两个连续区间。

那么我们把两个区间视为一个二维平面上的正方形+1+1+1,然后询问的视为查询一个点的值,实现方法就是把这些都离线下来用扫描线。

好现在考虑这一题,我们会发现一条路径上我们把单种颜色的拿出来,钥匙视为(((,宝箱视为))),那么就是一个类似括号匹配的东西,每一对产生贡献的点都会满足中间是一个合法的括号序。

那么我们从这个性质入手,我们枚举所有颜色,把同色的点建一棵虚树,对于每个钥匙我们暴力扫全图,能找到很多个合法的贡献对x,yx,yx,y,像上面的方法扫描线就好了。

实际上我们会发现这样枚举出来的贡献对其实是nnn个而不是5n5n5n个的。

时间复杂度:O((n+m)log⁡n)O((n+m)\log n)O((n+m)logn)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
using namespace std;
const int N=5e5+10;
struct node{int to,next;
}a[N<<1];
int n,m,tot,Top,cnt,ls[N],t[N],c[N],s[N],ans[N];
int siz[N],dep[N],son[N],fa[N],top[N],dfn[N],rfn[N],ed[N];
vector<int> G[N],p[N];stack<int> cl;
vector<pair<int,int> >I[N],O[N],q[N];
void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
bool cmp(int x,int y)
{return rfn[x]<rfn[y];}
void dfs(int x){siz[x]=1;dep[x]=dep[fa[x]]+1;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa[x])continue;fa[y]=x;dfs(y);siz[x]+=siz[y];if(siz[y]>siz[son[x]])son[x]=y;}return;
}
void dfs2(int x){dfn[++cnt]=x;rfn[x]=cnt;if(son[x]){top[son[x]]=top[x];dfs2(son[x]);}for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa[x]||y==son[x])continue;top[y]=y;dfs2(y);}ed[x]=cnt;return;
}
int LCA(int x,int y){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);x=fa[top[x]];}return (dep[x]<dep[y])?x:y;
}
int getTop(int x,int y){while(top[y]!=top[x])if(fa[top[y]]==x)return top[y];else y=fa[top[y]];return dfn[rfn[x]+1];
}
void addG(int x,int y){G[x].push_back(y);G[y].push_back(x);cl.push(x);cl.push(y);return;
}
void Clear(){Top=0;while(!cl.empty()){G[cl.top()].clear();cl.pop();}
}
void Ins(int x){if(!Top){s[++Top]=x;return;}int lca=LCA(s[Top],x);while(Top>1&&dep[s[Top-1]]>=dep[lca])addG(s[Top-1],s[Top]),Top--;if(dep[s[Top]]>dep[lca])addG(lca,s[Top]),Top--;if(s[Top]!=lca)s[++Top]=lca;s[++Top]=x;return;
}
void Build(vector<int> &p){sort(p.begin(),p.end(),cmp);if(p[0]!=1)Ins(1);for(int i=0;i<p.size();i++)Ins(p[i]);while(Top>1)addG(s[Top-1],s[Top]),Top--;
}
void Sets(int x,int y){int lca=LCA(x,y);if(lca==x){x=getTop(x,y);I[1].push_back(mp(rfn[y],ed[y]));O[rfn[x]].push_back(mp(rfn[y],ed[y]));I[ed[x]+1].push_back(mp(rfn[y],ed[y]));}else if(lca==y){y=getTop(y,x);if(rfn[y]>1)I[rfn[x]].push_back(mp(1,rfn[y]-1));if(ed[y]<n)I[rfn[x]].push_back(mp(ed[y]+1,n));if(rfn[y]>1)O[ed[x]+1].push_back(mp(1,rfn[y]-1));if(ed[y]<n)O[ed[x]+1].push_back(mp(ed[y]+1,n));}else{I[rfn[x]].push_back(mp(rfn[y],ed[y]));O[ed[x]+1].push_back(mp(rfn[y],ed[y]));}return;
}
void calc(int x,int fa,int k,int &from,int &_){if(c[x]==-_){k++;}if(c[x]==_){k--;if(!k){Sets(from,x);return;}}for(int i=0;i<G[x].size();i++)if(G[x][i]!=fa)calc(G[x][i],x,k,from,_);
}
void Change(int x,int val){while(x<=n){t[x]+=val;x+=lowbit(x);}return;
}
int Ask(int x){int ans=0;while(x){ans+=t[x];x-=lowbit(x);}return ans;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1,t;i<=n;i++){scanf("%d%d",&t,&c[i]);p[c[i]].push_back(i);if(t==1)c[i]=-c[i];}for(int i=1,x,y;i<n;i++){scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}dfs(1);dfs2(1);for(int _=1;_<=n;_++){if(p[_].empty())continue;Build(p[_]);for(int i=0;i<p[_].size();i++)if(c[p[_][i]]==-_)calc(p[_][i],0,0,p[_][i],_);Clear();}for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),q[rfn[x]].push_back(mp(rfn[y],i));for(int i=1;i<=n;i++){for(int j=0;j<I[i].size();j++)Change(I[i][j].first,1),Change(I[i][j].second+1,-1);for(int j=0;j<O[i].size();j++)Change(O[i][j].first,-1),Change(O[i][j].second+1,1);for(int j=0;j<q[i].size();j++)ans[q[i][j].second]=Ask(q[i][j].first);}for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);return 0;
}

P8339-[AHOI2022]钥匙【虚树,扫描线】相关推荐

  1. 2020牛客多校第一场B虚树+质数筛+换根dp

    题目大意: 1.可以发现阶乘增长是很快的所以你要把整颗树建立出来是不实际的. 2.我们可以假设这棵树已经建出来出来了我们应该怎么搞 首先很明显是一个树形dp, 我们设dp[j],是以j为u到其他点距离 ...

  2. 线段树扫描线求矩形周长详解

    线段树扫描线求矩形周长详解 原创 wucstdio 最后发布于2018-04-24 16:12:09 阅读数 841 收藏 发布于2018-04-24 16:12:09 版权声明:本文为博主原创文章, ...

  3. [WC2018]通道——边分治+虚树+树形DP

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

  4. Codeforces.809E.Surprise me!(莫比乌斯反演 虚树)

    题目链接 \(Description\) 给定一棵树,求\[\frac{1}{n(n-1)/2}\times\sum_{i\in[1,n],j\in[1,n],i\neq j}\varphi(a_i\ ...

  5. Luogu4606 SDOI2018 战略游戏 圆方树、虚树、链并

    传送门 弱化版 考虑到去掉一个点使得存在两个点不连通的形式类似割点,不难想到建立圆方树.那么在圆方树上对于给出的关键点建立虚树之后,我们需要求的就是虚树路径上所有圆点的数量减去关键点的数量. 因为没有 ...

  6. hdu3255 线段树扫描线求体积

    题意:       给你n个矩形,每个矩形上都有一个权值(该矩形单位面积的价值),矩形之间可能重叠,重叠部分的权值按照最大的算,最后问这n个矩形组成的图形的最大价值. 思路:       线段树扫描线 ...

  7. hdu1542 线段树扫描线求矩形面积的并

    题意:       给你n个正方形,求出他们的所占面积有多大,重叠的部分只能算一次. 思路:       自己的第一道线段树扫描线题目,至于扫描线,最近会写一个总结,现在就不直接在这里写了,说下我的方 ...

  8. hihoCoder #1954 : 压缩树(虚树)

    题意 有一棵 \(n\) 个节点且以 \(1\) 为根的树,把它复制成 \(m\) 个版本,有 \(q\) 次操作,每次对 \([l, r]\) 这些版本的 \(v\) 节点到根的路径收缩起来. 收缩 ...

  9. bzoj 3572 [Hnoi2014]世界树——虚树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3572 关于虚树:https://www.cnblogs.com/zzqsblog/p/556 ...

最新文章

  1. Nginx——upstream参数
  2. 干粮 -- 计算机程序设计艺术(The Art of Computer Design) - 2
  3. 两个div叠加触发事件发生闪烁问题
  4. CSS3自定义滚动条样式
  5. [Qt入门]QTableWidget控件创建
  6. F. Cheap Robot(kruskal 重构树)
  7. 程序型语言VS.编译型语言
  8. 七款最常用的PHP本地服务器
  9. 马斯克挽尊,回应为何电动皮卡车窗玻璃怼不过钢球...
  10. Ubuntu 16.04之标题栏实时显示上下行网速、CPU及内存使用率
  11. Mysql高级部分系列(一)
  12. 蓝牙版本avrcp怎么选_「科技犬」除了苹果AirPods,真无线蓝牙耳机到底怎么选?...
  13. GridView, DataGrid 中,DataFormatString语法汇总
  14. sobol灵敏度分析matlab_灵敏度分析 使用MATLAB编写
  15. 下列python语句的输出结果是_下列Python语句的输出结果是 __________ 。 print(数量%4d,单价%3.3f %(100,285.6)) (3.0分)_学小易找答案...
  16. 基于Halcon的高精度圆拟合算法思路
  17. 重置帆软决策系统用户名密码
  18. Three.js - 着色器材质(二十七)
  19. 夺宝观察:从一元夺宝用户的舆论看行业发展
  20. 查看终端设备接入交换机的端口方法

热门文章

  1. 服务器CPU作用是什么?
  2. Redis - 0、几款可视化工具
  3. axure变成一个小手了_Axure教程:这几个小技巧你一定要知道
  4. 如何将u盘两个分区合并?u盘怎么合并一个区
  5. 【Eclipse使用技巧】格式化代码的方法 + 解决注释是繁体字的方法
  6. 嵌入式Linux:安装Ubuntu系统环境
  7. Python中集合的常用操作
  8. 商家分账使用场景流程
  9. 通过Visual Studio 2019搭建DirectX 12开发环境
  10. Unity中Destory销毁对象不是立即销毁